diff options
Diffstat (limited to 'src/com/android/tv/dvr')
100 files changed, 6940 insertions, 5626 deletions
diff --git a/src/com/android/tv/dvr/BaseDvrDataManager.java b/src/com/android/tv/dvr/BaseDvrDataManager.java index a8637449..b8bffa18 100644 --- a/src/com/android/tv/dvr/BaseDvrDataManager.java +++ b/src/com/android/tv/dvr/BaseDvrDataManager.java @@ -23,15 +23,13 @@ import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.util.ArraySet; import android.util.Log; - import com.android.tv.common.SoftPreconditions; import com.android.tv.common.feature.CommonFeatures; +import com.android.tv.common.util.Clock; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.ScheduledRecording.RecordingState; import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.util.Clock; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,14 +40,12 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; -/** - * Base implementation of @{link DataManagerInternal}. - */ +/** Base implementation of @{link DataManagerInternal}. */ @MainThread @TargetApi(Build.VERSION_CODES.N) public abstract class BaseDvrDataManager implements WritableDvrDataManager { - private final static String TAG = "BaseDvrDataManager"; - private final static boolean DEBUG = false; + private static final String TAG = "BaseDvrDataManager"; + private static final boolean DEBUG = false; protected final Clock mClock; private final Set<OnDvrScheduleLoadFinishedListener> mOnDvrScheduleLoadFinishedListeners = @@ -61,7 +57,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { private final Set<RecordedProgramListener> mRecordedProgramListeners = new ArraySet<>(); private final HashMap<Long, ScheduledRecording> mDeletedScheduleMap = new HashMap<>(); - BaseDvrDataManager(Context context, Clock clock) { + public BaseDvrDataManager(Context context, Clock clock) { SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG); mClock = clock; } @@ -129,8 +125,8 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } /** - * Calls {@link OnRecordedProgramLoadFinishedListener#onRecordedProgramLoadFinished()} - * for each listener. + * Calls {@link OnRecordedProgramLoadFinishedListener#onRecordedProgramLoadFinished()} for each + * listener. */ protected final void notifyRecordedProgramLoadFinished() { for (OnRecordedProgramLoadFinishedListener l : mOnRecordedProgramLoadFinishedListeners) { @@ -139,10 +135,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link RecordedProgramListener#onRecordedProgramsAdded} - * for each listener. - */ + /** Calls {@link RecordedProgramListener#onRecordedProgramsAdded} for each listener. */ protected final void notifyRecordedProgramsAdded(RecordedProgram... recordedPrograms) { for (RecordedProgramListener l : mRecordedProgramListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(recordedPrograms)); @@ -150,10 +143,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link RecordedProgramListener#onRecordedProgramsChanged} - * for each listener. - */ + /** Calls {@link RecordedProgramListener#onRecordedProgramsChanged} for each listener. */ protected final void notifyRecordedProgramsChanged(RecordedProgram... recordedPrograms) { for (RecordedProgramListener l : mRecordedProgramListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(recordedPrograms)); @@ -161,10 +151,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link RecordedProgramListener#onRecordedProgramsRemoved} - * for each listener. - */ + /** Calls {@link RecordedProgramListener#onRecordedProgramsRemoved} for each listener. */ protected final void notifyRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { for (RecordedProgramListener l : mRecordedProgramListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(recordedPrograms)); @@ -172,10 +159,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link SeriesRecordingListener#onSeriesRecordingAdded} - * for each listener. - */ + /** Calls {@link SeriesRecordingListener#onSeriesRecordingAdded} for each listener. */ protected final void notifySeriesRecordingAdded(SeriesRecording... seriesRecordings) { for (SeriesRecordingListener l : mSeriesRecordingListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(seriesRecordings)); @@ -183,10 +167,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link SeriesRecordingListener#onSeriesRecordingRemoved} - * for each listener. - */ + /** Calls {@link SeriesRecordingListener#onSeriesRecordingRemoved} for each listener. */ protected final void notifySeriesRecordingRemoved(SeriesRecording... seriesRecordings) { for (SeriesRecordingListener l : mSeriesRecordingListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(seriesRecordings)); @@ -194,11 +175,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls - * {@link SeriesRecordingListener#onSeriesRecordingChanged} - * for each listener. - */ + /** Calls {@link SeriesRecordingListener#onSeriesRecordingChanged} for each listener. */ protected final void notifySeriesRecordingChanged(SeriesRecording... seriesRecordings) { for (SeriesRecordingListener l : mSeriesRecordingListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(seriesRecordings)); @@ -206,10 +183,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} - * for each listener. - */ + /** Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. */ protected final void notifyScheduledRecordingAdded(ScheduledRecording... scheduledRecording) { for (ScheduledRecordingListener l : mScheduledRecordingListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(scheduledRecording)); @@ -217,10 +191,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } } - /** - * Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} - * for each listener. - */ + /** Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. */ protected final void notifyScheduledRecordingRemoved(ScheduledRecording... scheduledRecording) { for (ScheduledRecordingListener l : mScheduledRecordingListeners) { if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(scheduledRecording)); @@ -229,9 +200,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } /** - * Calls - * {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged} - * for each listener. + * Calls {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged} for each listener. */ protected final void notifyScheduledRecordingStatusChanged( ScheduledRecording... scheduledRecording) { @@ -257,28 +226,47 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { @Override public List<ScheduledRecording> getAvailableScheduledRecordings() { - return filterEndTimeIsPast(getRecordingsWithState( - ScheduledRecording.STATE_RECORDING_IN_PROGRESS, - ScheduledRecording.STATE_RECORDING_NOT_STARTED)); + return filterEndTimeIsPast( + getRecordingsWithState( + ScheduledRecording.STATE_RECORDING_IN_PROGRESS, + ScheduledRecording.STATE_RECORDING_NOT_STARTED)); } @Override public List<ScheduledRecording> getStartedRecordings() { - return filterEndTimeIsPast(getRecordingsWithState( - ScheduledRecording.STATE_RECORDING_IN_PROGRESS)); + return filterEndTimeIsPast( + getRecordingsWithState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS)); } @Override public List<ScheduledRecording> getNonStartedScheduledRecordings() { - return filterEndTimeIsPast(getRecordingsWithState( - ScheduledRecording.STATE_RECORDING_NOT_STARTED)); + return filterEndTimeIsPast( + getRecordingsWithState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)); + } + + @Override + public List<ScheduledRecording> getFailedScheduledRecordings() { + return getRecordingsWithState(ScheduledRecording.STATE_RECORDING_FAILED); } @Override public void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState) { if (scheduledRecording.getState() != newState) { - updateScheduledRecording(ScheduledRecording.buildFrom(scheduledRecording) - .setState(newState).build()); + updateScheduledRecording( + ScheduledRecording.buildFrom(scheduledRecording).setState(newState).build()); + } + } + + @Override + public void changeState( + ScheduledRecording scheduledRecording, @RecordingState int newState, int reason) { + if (scheduledRecording.getState() != newState) { + ScheduledRecording.Builder builder = + ScheduledRecording.buildFrom(scheduledRecording).setState(newState); + if (newState == ScheduledRecording.STATE_RECORDING_FAILED) { + builder.setFailedReason(reason); + } + updateScheduledRecording(builder.build()); } } @@ -300,9 +288,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { return mDeletedScheduleMap; } - /** - * Returns the schedules whose state is contained by states. - */ + /** Returns the schedules whose state is contained by states. */ protected abstract List<ScheduledRecording> getRecordingsWithState(int... states); @Override @@ -357,5 +343,5 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager { } @Override - public void forgetStorage(String inputId) { } + public void forgetStorage(String inputId) {} } diff --git a/src/com/android/tv/dvr/DvrDataManager.java b/src/com/android/tv/dvr/DvrDataManager.java index 6d400b82..10dfc4c9 100644 --- a/src/com/android/tv/dvr/DvrDataManager.java +++ b/src/com/android/tv/dvr/DvrDataManager.java @@ -20,48 +20,36 @@ import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Range; - import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.ScheduledRecording.RecordingState; import com.android.tv.dvr.data.SeriesRecording; - import java.util.Collection; import java.util.List; -/** - * Read only data manager. - */ +/** Read only data manager. */ @MainThread public interface DvrDataManager { long NEXT_START_TIME_NOT_FOUND = -1; boolean isInitialized(); - /** - * Returns {@code true} if the schedules were loaded, otherwise {@code false}. - */ + /** Returns {@code true} if the schedules were loaded, otherwise {@code false}. */ boolean isDvrScheduleLoadFinished(); - /** - * Returns {@code true} if the recorded programs were loaded, otherwise {@code false}. - */ + /** Returns {@code true} if the recorded programs were loaded, otherwise {@code false}. */ boolean isRecordedProgramLoadFinished(); - /** - * Returns past recordings. - */ + /** Returns past recordings. */ List<RecordedProgram> getRecordedPrograms(); - /** - * Returns past recorded programs in the given series. - */ + /** Returns past recorded programs in the given series. */ List<RecordedProgram> getRecordedPrograms(long seriesRecordingId); /** * Returns all {@link ScheduledRecording} regardless of state. - * <p> - * The result doesn't contain the deleted schedules. + * + * <p>The result doesn't contain the deleted schedules. */ List<ScheduledRecording> getAllScheduledRecordings(); @@ -71,29 +59,24 @@ public interface DvrDataManager { */ List<ScheduledRecording> getAvailableScheduledRecordings(); - /** - * Returns started recordings that expired. - */ + /** Returns started recordings that expired. */ List<ScheduledRecording> getStartedRecordings(); - /** - * Returns scheduled but not started recordings that have not expired. - */ + /** Returns scheduled but not started recordings that have not expired. */ List<ScheduledRecording> getNonStartedScheduledRecordings(); - /** - * Returns series recordings. - */ + /** Returns failed recordings. */ + List<ScheduledRecording> getFailedScheduledRecordings(); + + /** Returns series recordings. */ List<SeriesRecording> getSeriesRecordings(); - /** - * Returns series recordings from the given input. - */ + /** Returns series recordings from the given input. */ List<SeriesRecording> getSeriesRecordings(String inputId); /** - * Returns the next start time after {@code time} or {@link #NEXT_START_TIME_NOT_FOUND} - * if none is found. + * Returns the next start time after {@code time} or {@link #NEXT_START_TIME_NOT_FOUND} if none + * is found. * * @param time time milliseconds */ @@ -103,73 +86,48 @@ public interface DvrDataManager { * Returns a list of the schedules with a overlap with the given time period inclusive and with * the given state. * - * <p> A recording overlaps with a period when - * {@code recording.getStartTime() <= period.getUpper() && - * recording.getEndTime() >= period.getLower()}. + * <p>A recording overlaps with a period when {@code recording.getStartTime() <= + * period.getUpper() && recording.getEndTime() >= period.getLower()}. * * @param period a time period in milliseconds. * @param state the state of the schedule. */ List<ScheduledRecording> getScheduledRecordings(Range<Long> period, @RecordingState int state); - /** - * Returns a list of the schedules in the given series. - */ + /** Returns a list of the schedules in the given series. */ List<ScheduledRecording> getScheduledRecordings(long seriesRecordingId); - /** - * Returns a list of the schedules from the given input. - */ + /** Returns a list of the schedules from the given input. */ List<ScheduledRecording> getScheduledRecordings(String inputId); - /** - * Add a {@link OnDvrScheduleLoadFinishedListener}. - */ + /** Add a {@link OnDvrScheduleLoadFinishedListener}. */ void addDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener); - /** - * Remove a {@link OnDvrScheduleLoadFinishedListener}. - */ + /** Remove a {@link OnDvrScheduleLoadFinishedListener}. */ void removeDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener); - /** - * Add a {@link OnRecordedProgramLoadFinishedListener}. - */ + /** Add a {@link OnRecordedProgramLoadFinishedListener}. */ void addRecordedProgramLoadFinishedListener(OnRecordedProgramLoadFinishedListener listener); - /** - * Remove a {@link OnRecordedProgramLoadFinishedListener}. - */ + /** Remove a {@link OnRecordedProgramLoadFinishedListener}. */ void removeRecordedProgramLoadFinishedListener(OnRecordedProgramLoadFinishedListener listener); - /** - * Add a {@link ScheduledRecordingListener}. - */ + /** Add a {@link ScheduledRecordingListener}. */ void addScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener); - /** - * Remove a {@link ScheduledRecordingListener}. - */ + /** Remove a {@link ScheduledRecordingListener}. */ void removeScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener); - /** - * Add a {@link RecordedProgramListener}. - */ + /** Add a {@link RecordedProgramListener}. */ void addRecordedProgramListener(RecordedProgramListener listener); - /** - * Remove a {@link RecordedProgramListener}. - */ + /** Remove a {@link RecordedProgramListener}. */ void removeRecordedProgramListener(RecordedProgramListener listener); - /** - * Add a {@link ScheduledRecordingListener}. - */ + /** Add a {@link ScheduledRecordingListener}. */ void addSeriesRecordingListener(SeriesRecordingListener seriesRecordingListener); - /** - * Remove a {@link ScheduledRecordingListener}. - */ + /** Remove a {@link ScheduledRecordingListener}. */ void removeSeriesRecordingListener(SeriesRecordingListener seriesRecordingListener); /** @@ -178,65 +136,47 @@ public interface DvrDataManager { @Nullable ScheduledRecording getScheduledRecording(long recordingId); - /** - * Returns the scheduled recording program with the given programId or null if is not found. - */ + /** Returns the scheduled recording program with the given programId or null if is not found. */ @Nullable ScheduledRecording getScheduledRecordingForProgramId(long programId); - /** - * Returns the recorded program with the given recordingId or null if is not found. - */ + /** Returns the recorded program with the given recordingId or null if is not found. */ @Nullable RecordedProgram getRecordedProgram(long recordingId); - /** - * Returns the series recording with the given seriesId or null if is not found. - */ + /** Returns the series recording with the given seriesId or null if is not found. */ @Nullable SeriesRecording getSeriesRecording(long seriesRecordingId); - /** - * Returns the series recording with the given series ID or {@code null} if not found. - */ + /** Returns the series recording with the given series ID or {@code null} if not found. */ @Nullable SeriesRecording getSeriesRecording(String seriesId); - /** - * Returns the schedules which are marked deleted. - */ + /** Returns the schedules which are marked deleted. */ Collection<ScheduledRecording> getDeletedSchedules(); - /** - * Returns the program IDs which is not allowed to make a schedule automatically. - */ + /** Returns the program IDs which is not allowed to make a schedule automatically. */ @NonNull Collection<Long> getDisallowedProgramIds(); /** - * Checks each of the give series recordings to see if it's empty, i.e., it doesn't contains - * any available schedules or recorded programs, and it's status is - * {@link SeriesRecording#STATE_SERIES_STOPPED}; and removes those empty series recordings. + * Checks each of the give series recordings to see if it's empty, i.e., it doesn't contains any + * available schedules or recorded programs, and it's status is {@link + * SeriesRecording#STATE_SERIES_STOPPED}; and removes those empty series recordings. */ void checkAndRemoveEmptySeriesRecording(long... seriesRecordingIds); - /** - * Listens for the DVR schedules loading finished. - */ + /** Listens for the DVR schedules loading finished. */ interface OnDvrScheduleLoadFinishedListener { void onDvrScheduleLoadFinished(); } - /** - * Listens for the recorded program loading finished. - */ + /** Listens for the recorded program loading finished. */ interface OnRecordedProgramLoadFinishedListener { void onRecordedProgramLoadFinished(); } - /** - * Listens for changes to {@link ScheduledRecording}s. - */ + /** Listens for changes to {@link ScheduledRecording}s. */ interface ScheduledRecordingListener { void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings); @@ -250,9 +190,7 @@ public interface DvrDataManager { void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings); } - /** - * Listens for changes to {@link SeriesRecording}s. - */ + /** Listens for changes to {@link SeriesRecording}s. */ interface SeriesRecordingListener { void onSeriesRecordingAdded(SeriesRecording... seriesRecordings); @@ -261,9 +199,7 @@ public interface DvrDataManager { void onSeriesRecordingChanged(SeriesRecording... seriesRecordings); } - /** - * Listens for changes to {@link RecordedProgram}s. - */ + /** Listens for changes to {@link RecordedProgram}s. */ interface RecordedProgramListener { void onRecordedProgramsAdded(RecordedProgram... recordedPrograms); diff --git a/src/com/android/tv/dvr/DvrDataManagerImpl.java b/src/com/android/tv/dvr/DvrDataManagerImpl.java index 6094ca72..2b4ecbf5 100644 --- a/src/com/android/tv/dvr/DvrDataManagerImpl.java +++ b/src/com/android/tv/dvr/DvrDataManagerImpl.java @@ -38,10 +38,12 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.Range; - -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.dvr.DvrStorageStatusManager.OnStorageMountChangedListener; +import com.android.tv.common.recording.RecordingStorageStatusManager; +import com.android.tv.common.recording.RecordingStorageStatusManager.OnStorageMountChangedListener; +import com.android.tv.common.util.Clock; +import com.android.tv.common.util.CommonUtils; import com.android.tv.dvr.data.IdGenerator; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.ScheduledRecording; @@ -59,12 +61,9 @@ import com.android.tv.dvr.provider.DvrDbSync; import com.android.tv.dvr.recorder.SeriesRecordingScheduler; import com.android.tv.util.AsyncDbTask; import com.android.tv.util.AsyncDbTask.AsyncRecordedProgramQueryTask; -import com.android.tv.util.Clock; import com.android.tv.util.Filter; import com.android.tv.util.TvInputManagerHelper; import com.android.tv.util.TvUriMatcher; -import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -73,10 +72,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; -/** - * DVR Data manager to handle recordings and schedules. - */ +/** DVR Data manager to handle recordings and schedules. */ @MainThread @TargetApi(Build.VERSION_CODES.N) public class DvrDataManagerImpl extends BaseDvrDataManager { @@ -98,52 +96,54 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { private final HashMap<Long, SeriesRecording> mSeriesRecordingsForRemovedInput = new HashMap<>(); private final Context mContext; - private final ContentObserver mContentObserver = new ContentObserver(new Handler( - Looper.getMainLooper())) { - @Override - public void onChange(boolean selfChange) { - onChange(selfChange, null); - } + private Executor mDbExecutor; + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + onChange(selfChange, null); + } - @Override - public void onChange(boolean selfChange, final @Nullable Uri uri) { - RecordedProgramsQueryTask task = new RecordedProgramsQueryTask( - mContext.getContentResolver(), uri); - task.executeOnDbThread(); - mPendingTasks.add(task); - } - }; + @Override + public void onChange(boolean selfChange, final @Nullable Uri uri) { + RecordedProgramsQueryTask task = + new RecordedProgramsQueryTask(mContext.getContentResolver(), uri); + task.executeOnDbThread(); + mPendingTasks.add(task); + } + }; private boolean mDvrLoadFinished; private boolean mRecordedProgramLoadFinished; private final Set<AsyncTask> mPendingTasks = new ArraySet<>(); private DvrDbSync mDbSync; - private DvrStorageStatusManager mStorageStatusManager; + private RecordingStorageStatusManager mStorageStatusManager; - private final TvInputCallback mInputCallback = new TvInputCallback() { - @Override - public void onInputAdded(String inputId) { - if (DEBUG) Log.d(TAG, "onInputAdded " + inputId); - if (!isInputAvailable(inputId)) { - if (DEBUG) Log.d(TAG, "Not available for recording"); - return; - } - unhideInput(inputId); - } + private final TvInputCallback mInputCallback = + new TvInputCallback() { + @Override + public void onInputAdded(String inputId) { + if (DEBUG) Log.d(TAG, "onInputAdded " + inputId); + if (!isInputAvailable(inputId)) { + if (DEBUG) Log.d(TAG, "Not available for recording"); + return; + } + unhideInput(inputId); + } - @Override - public void onInputRemoved(String inputId) { - if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId); - hideInput(inputId); - } - }; + @Override + public void onInputRemoved(String inputId) { + if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId); + hideInput(inputId); + } + }; private final OnStorageMountChangedListener mStorageMountChangedListener = new OnStorageMountChangedListener() { @Override public void onStorageMountChanged(boolean storageMounted) { for (TvInputInfo input : mInputManager.getTvInputInfos(true, true)) { - if (Utils.isBundledInput(input.getId())) { + if (CommonUtils.isBundledInput(input.getId())) { if (storageMounted) { unhideInput(input.getId()); } else { @@ -154,8 +154,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } }; - private static <T> List<T> moveElements(HashMap<Long, T> from, HashMap<Long, T> to, - Filter<T> filter) { + private static <T> List<T> moveElements( + HashMap<Long, T> from, HashMap<Long, T> to, Filter<T> filter) { List<T> moved = new ArrayList<>(); Iterator<Entry<Long, T>> iter = from.entrySet().iterator(); while (iter.hasNext()) { @@ -172,119 +172,139 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { public DvrDataManagerImpl(Context context, Clock clock) { super(context, clock); mContext = context; - mInputManager = TvApplication.getSingletons(context).getTvInputManagerHelper(); - mStorageStatusManager = TvApplication.getSingletons(context).getDvrStorageStatusManager(); + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + mInputManager = tvSingletons.getTvInputManagerHelper(); + mStorageStatusManager = tvSingletons.getRecordingStorageStatusManager(); + mDbExecutor = tvSingletons.getDbExecutor(); } public void start() { mInputManager.addCallback(mInputCallback); mStorageStatusManager.addListener(mStorageMountChangedListener); - AsyncDvrQuerySeriesRecordingTask dvrQuerySeriesRecordingTask - = new AsyncDvrQuerySeriesRecordingTask(mContext) { - @Override - protected void onCancelled(List<SeriesRecording> seriesRecordings) { - mPendingTasks.remove(this); - } + AsyncDvrQuerySeriesRecordingTask dvrQuerySeriesRecordingTask = + new AsyncDvrQuerySeriesRecordingTask(mContext) { + @Override + protected void onCancelled(List<SeriesRecording> seriesRecordings) { + mPendingTasks.remove(this); + } - @Override - protected void onPostExecute(List<SeriesRecording> seriesRecordings) { - mPendingTasks.remove(this); - long maxId = 0; - HashSet<String> seriesIds = new HashSet<>(); - for (SeriesRecording r : seriesRecordings) { - if (SoftPreconditions.checkState(!seriesIds.contains(r.getSeriesId()), TAG, - "Skip loading series recording with duplicate series ID: " + r)) { - seriesIds.add(r.getSeriesId()); - if (isInputAvailable(r.getInputId())) { - mSeriesRecordings.put(r.getId(), r); - mSeriesId2SeriesRecordings.put(r.getSeriesId(), r); - } else { - mSeriesRecordingsForRemovedInput.put(r.getId(), r); + @Override + protected void onPostExecute(List<SeriesRecording> seriesRecordings) { + mPendingTasks.remove(this); + long maxId = 0; + HashSet<String> seriesIds = new HashSet<>(); + for (SeriesRecording r : seriesRecordings) { + if (SoftPreconditions.checkState( + !seriesIds.contains(r.getSeriesId()), + TAG, + "Skip loading series recording with duplicate series ID: " + + r)) { + seriesIds.add(r.getSeriesId()); + if (isInputAvailable(r.getInputId())) { + mSeriesRecordings.put(r.getId(), r); + mSeriesId2SeriesRecordings.put(r.getSeriesId(), r); + } else { + mSeriesRecordingsForRemovedInput.put(r.getId(), r); + } + } + if (maxId < r.getId()) { + maxId = r.getId(); + } } + IdGenerator.SERIES_RECORDING.setMaxId(maxId); } - if (maxId < r.getId()) { - maxId = r.getId(); - } - } - IdGenerator.SERIES_RECORDING.setMaxId(maxId); - } - }; + }; dvrQuerySeriesRecordingTask.executeOnDbThread(); mPendingTasks.add(dvrQuerySeriesRecordingTask); - AsyncDvrQueryScheduleTask dvrQueryScheduleTask - = new AsyncDvrQueryScheduleTask(mContext) { - @Override - protected void onCancelled(List<ScheduledRecording> scheduledRecordings) { - mPendingTasks.remove(this); - } + AsyncDvrQueryScheduleTask dvrQueryScheduleTask = + new AsyncDvrQueryScheduleTask(mContext) { + @Override + protected void onCancelled(List<ScheduledRecording> scheduledRecordings) { + mPendingTasks.remove(this); + } - @SuppressLint("SwitchIntDef") - @Override - protected void onPostExecute(List<ScheduledRecording> result) { - mPendingTasks.remove(this); - long maxId = 0; - List<SeriesRecording> seriesRecordingsToAdd = new ArrayList<>(); - List<ScheduledRecording> toUpdate = new ArrayList<>(); - List<ScheduledRecording> toDelete = new ArrayList<>(); - for (ScheduledRecording r : result) { - if (!isInputAvailable(r.getInputId())) { - mScheduledRecordingsForRemovedInput.put(r.getId(), r); - } else if (r.getState() == ScheduledRecording.STATE_RECORDING_DELETED) { - getDeletedScheduleMap().put(r.getProgramId(), r); - } else { - mScheduledRecordings.put(r.getId(), r); - if (r.getProgramId() != ScheduledRecording.ID_NOT_SET) { - mProgramId2ScheduledRecordings.put(r.getProgramId(), r); - } - // Adjust the state of the schedules before DB loading is finished. - switch (r.getState()) { - case ScheduledRecording.STATE_RECORDING_IN_PROGRESS: - if (r.getEndTimeMs() <= mClock.currentTimeMillis()) { - toUpdate.add(ScheduledRecording.buildFrom(r) - .setState(ScheduledRecording.STATE_RECORDING_FAILED) - .build()); - } else { - toUpdate.add(ScheduledRecording.buildFrom(r) - .setState( - ScheduledRecording.STATE_RECORDING_NOT_STARTED) - .build()); + @SuppressLint("SwitchIntDef") + @Override + protected void onPostExecute(List<ScheduledRecording> result) { + mPendingTasks.remove(this); + long maxId = 0; + int reasonNotStarted = + ScheduledRecording + .FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED; + List<ScheduledRecording> toUpdate = new ArrayList<>(); + List<ScheduledRecording> toDelete = new ArrayList<>(); + for (ScheduledRecording r : result) { + if (!isInputAvailable(r.getInputId())) { + mScheduledRecordingsForRemovedInput.put(r.getId(), r); + } else if (r.getState() == ScheduledRecording.STATE_RECORDING_DELETED) { + getDeletedScheduleMap().put(r.getProgramId(), r); + } else { + mScheduledRecordings.put(r.getId(), r); + if (r.getProgramId() != ScheduledRecording.ID_NOT_SET) { + mProgramId2ScheduledRecordings.put(r.getProgramId(), r); } - break; - case ScheduledRecording.STATE_RECORDING_NOT_STARTED: - if (r.getEndTimeMs() <= mClock.currentTimeMillis()) { - toUpdate.add(ScheduledRecording.buildFrom(r) - .setState(ScheduledRecording.STATE_RECORDING_FAILED) - .build()); + // Adjust the state of the schedules before DB loading is finished. + switch (r.getState()) { + case ScheduledRecording.STATE_RECORDING_IN_PROGRESS: + if (r.getEndTimeMs() <= mClock.currentTimeMillis()) { + int reason = + ScheduledRecording.FAILED_REASON_NOT_FINISHED; + toUpdate.add( + ScheduledRecording.buildFrom(r) + .setState( + ScheduledRecording + .STATE_RECORDING_FAILED) + .setFailedReason(reason) + .build()); + } else { + toUpdate.add( + ScheduledRecording.buildFrom(r) + .setState( + ScheduledRecording + .STATE_RECORDING_NOT_STARTED) + .build()); + } + break; + case ScheduledRecording.STATE_RECORDING_NOT_STARTED: + if (r.getEndTimeMs() <= mClock.currentTimeMillis()) { + toUpdate.add( + ScheduledRecording.buildFrom(r) + .setState( + ScheduledRecording + .STATE_RECORDING_FAILED) + .setFailedReason(reasonNotStarted) + .build()); + } + break; + case ScheduledRecording.STATE_RECORDING_CANCELED: + toDelete.add(r); + break; + default: // fall out } - break; - case ScheduledRecording.STATE_RECORDING_CANCELED: - toDelete.add(r); - break; + } + if (maxId < r.getId()) { + maxId = r.getId(); + } + } + if (!toUpdate.isEmpty()) { + updateScheduledRecording(ScheduledRecording.toArray(toUpdate)); + } + if (!toDelete.isEmpty()) { + removeScheduledRecording(ScheduledRecording.toArray(toDelete)); + } + IdGenerator.SCHEDULED_RECORDING.setMaxId(maxId); + if (mRecordedProgramLoadFinished) { + validateSeriesRecordings(); + } + mDvrLoadFinished = true; + notifyDvrScheduleLoadFinished(); + if (isInitialized()) { + mDbSync = new DvrDbSync(mContext, DvrDataManagerImpl.this); + mDbSync.start(); + SeriesRecordingScheduler.getInstance(mContext).start(); } } - if (maxId < r.getId()) { - maxId = r.getId(); - } - } - if (!toUpdate.isEmpty()) { - updateScheduledRecording(ScheduledRecording.toArray(toUpdate)); - } - if (!toDelete.isEmpty()) { - removeScheduledRecording(ScheduledRecording.toArray(toDelete)); - } - IdGenerator.SCHEDULED_RECORDING.setMaxId(maxId); - if (mRecordedProgramLoadFinished) { - validateSeriesRecordings(); - } - mDvrLoadFinished = true; - notifyDvrScheduleLoadFinished(); - if (isInitialized()) { - mDbSync = new DvrDbSync(mContext, DvrDataManagerImpl.this); - mDbSync.start(); - SeriesRecordingScheduler.getInstance(mContext).start(); - } - } - }; + }; dvrQueryScheduleTask.executeOnDbThread(); mPendingTasks.add(dvrQueryScheduleTask); RecordedProgramsQueryTask mRecordedProgramQueryTask = @@ -341,8 +361,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { mRecordedProgramsForRemovedInput.clear(); notifyRecordedProgramsRemoved(RecordedProgram.toArray(oldRecordedPrograms)); } else { - HashMap<Long, RecordedProgram> oldRecordedPrograms - = new HashMap<>(mRecordedPrograms); + HashMap<Long, RecordedProgram> oldRecordedPrograms = + new HashMap<>(mRecordedPrograms); mRecordedPrograms.clear(); mRecordedProgramsForRemovedInput.clear(); List<RecordedProgram> addedRecordedPrograms = new ArrayList<>(); @@ -492,7 +512,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } @VisibleForTesting - static long getNextStartTimeAfter(List<ScheduledRecording> scheduledRecordings, long startTime) { + static long getNextStartTimeAfter( + List<ScheduledRecording> scheduledRecordings, long startTime) { int start = 0; int end = scheduledRecordings.size() - 1; while (start <= end) { @@ -503,13 +524,14 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { end = mid - 1; } } - return start < scheduledRecordings.size() ? scheduledRecordings.get(start).getStartTimeMs() + return start < scheduledRecordings.size() + ? scheduledRecordings.get(start).getStartTimeMs() : NEXT_START_TIME_NOT_FOUND; } @Override - public List<ScheduledRecording> getScheduledRecordings(Range<Long> period, - @RecordingState int state) { + public List<ScheduledRecording> getScheduledRecordings( + Range<Long> period, @RecordingState int state) { List<ScheduledRecording> result = new ArrayList<>(); for (ScheduledRecording r : mScheduledRecordings.values()) { if (r.isOverLapping(period) && r.getState() == state) { @@ -595,8 +617,11 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { r.setId(IdGenerator.SERIES_RECORDING.newId()); mSeriesRecordings.put(r.getId(), r); SeriesRecording previousSeries = mSeriesId2SeriesRecordings.put(r.getSeriesId(), r); - SoftPreconditions.checkArgument(previousSeries == null, TAG, "Attempt to add series" - + " recording with the duplicate series ID: " + r.getSeriesId()); + SoftPreconditions.checkArgument( + previousSeries == null, + TAG, + "Attempt to add series" + " recording with the duplicate series ID: %s", + r.getSeriesId()); } if (mDvrLoadFinished) { notifySeriesRecordingAdded(seriesRecordings); @@ -620,20 +645,23 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { mProgramId2ScheduledRecordings.remove(r.getProgramId()); if (r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET && (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { + || r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { seriesRecordingIdsToCheck.add(r.getSeriesRecordingId()); } boolean isScheduleForRemovedInput = mScheduledRecordingsForRemovedInput.remove(r.getProgramId()) != null; // If it belongs to the series recording and it's not started yet, just mark delete // instead of deleting it. - if (!isScheduleForRemovedInput && !forceRemove + if (!isScheduleForRemovedInput + && !forceRemove && r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET && (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || r.getState() == ScheduledRecording.STATE_RECORDING_CANCELED)) { + || r.getState() == ScheduledRecording.STATE_RECORDING_CANCELED)) { SoftPreconditions.checkState(r.getProgramId() != ScheduledRecording.ID_NOT_SET); - ScheduledRecording deleted = ScheduledRecording.buildFrom(r) - .setState(ScheduledRecording.STATE_RECORDING_DELETED).build(); + ScheduledRecording deleted = + ScheduledRecording.buildFrom(r) + .setState(ScheduledRecording.STATE_RECORDING_DELETED) + .build(); getDeletedScheduleMap().put(deleted.getProgramId(), deleted); schedulesNotToDelete.add(deleted); } else { @@ -655,12 +683,12 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } } if (!schedulesToDelete.isEmpty()) { - new AsyncDeleteScheduleTask(mContext).executeOnDbThread( - ScheduledRecording.toArray(schedulesToDelete)); + new AsyncDeleteScheduleTask(mContext) + .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete)); } if (!schedulesNotToDelete.isEmpty()) { - new AsyncUpdateScheduleTask(mContext).executeOnDbThread( - ScheduledRecording.toArray(schedulesNotToDelete)); + new AsyncUpdateScheduleTask(mContext) + .executeOnDbThread(ScheduledRecording.toArray(schedulesNotToDelete)); } } @@ -680,8 +708,10 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { if (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED) { toDelete.add(r); } else { - toUpdate.add(ScheduledRecording.buildFrom(r) - .setSeriesRecordingId(SeriesRecording.ID_NOT_SET).build()); + toUpdate.add( + ScheduledRecording.buildFrom(r) + .setSeriesRecordingId(SeriesRecording.ID_NOT_SET) + .build()); } } } @@ -709,7 +739,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { List<ScheduledRecording> toUpdate = new ArrayList<>(); Set<Long> seriesRecordingIdsToCheck = new HashSet<>(); for (ScheduledRecording r : schedules) { - if (!SoftPreconditions.checkState(mScheduledRecordings.containsKey(r.getId()), TAG, + if (!SoftPreconditions.checkState( + mScheduledRecordings.containsKey(r.getId()), + TAG, "Recording not found for: " + r)) { continue; } @@ -720,8 +752,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { long programId = r.getProgramId(); if (oldScheduledRecording.getProgramId() != programId && oldScheduledRecording.getProgramId() != ScheduledRecording.ID_NOT_SET) { - ScheduledRecording oldValueForProgramId = mProgramId2ScheduledRecordings - .get(oldScheduledRecording.getProgramId()); + ScheduledRecording oldValueForProgramId = + mProgramId2ScheduledRecordings.get(oldScheduledRecording.getProgramId()); if (oldValueForProgramId.getId() == r.getId()) { // Only remove the old ScheduledRecording if it has the same ID as the new one. mProgramId2ScheduledRecordings.remove(oldScheduledRecording.getProgramId()); @@ -755,14 +787,17 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { @Override public void updateSeriesRecording(final SeriesRecording... seriesRecordings) { for (SeriesRecording r : seriesRecordings) { - if (!SoftPreconditions.checkArgument(mSeriesRecordings.containsKey(r.getId()), TAG, - "Non Existing Series ID: " + r)) { + if (!SoftPreconditions.checkArgument( + mSeriesRecordings.containsKey(r.getId()), + TAG, + "Non Existing Series ID: %s", + r)) { continue; } SeriesRecording old1 = mSeriesRecordings.put(r.getId(), r); SeriesRecording old2 = mSeriesId2SeriesRecordings.put(r.getSeriesId(), r); - SoftPreconditions.checkArgument(old1.equals(old2), TAG, "Series ID cannot be" - + " updated: " + r); + SoftPreconditions.checkArgument( + old1.equals(old2), TAG, "Series ID cannot be updated: %s", r); } if (mDvrLoadFinished) { notifySeriesRecordingChanged(seriesRecordings); @@ -772,7 +807,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { private boolean isInputAvailable(String inputId) { return mInputManager.hasTvInputInfo(inputId) - && (!Utils.isBundledInput(inputId) || mStorageStatusManager.isStorageMounted()); + && (!CommonUtils.isBundledInput(inputId) + || mStorageStatusManager.isStorageMounted()); } private void removeDeletedSchedules(ScheduledRecording... addedSchedules) { @@ -784,8 +820,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } } if (!schedulesToDelete.isEmpty()) { - new AsyncDeleteScheduleTask(mContext).executeOnDbThread( - ScheduledRecording.toArray(schedulesToDelete)); + new AsyncDeleteScheduleTask(mContext) + .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete)); } } @@ -805,15 +841,17 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } } if (!schedulesToDelete.isEmpty()) { - new AsyncDeleteScheduleTask(mContext).executeOnDbThread( - ScheduledRecording.toArray(schedulesToDelete)); + new AsyncDeleteScheduleTask(mContext) + .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete)); } } private void unhideInput(String inputId) { if (DEBUG) Log.d(TAG, "unhideInput " + inputId); List<ScheduledRecording> movedSchedules = - moveElements(mScheduledRecordingsForRemovedInput, mScheduledRecordings, + moveElements( + mScheduledRecordingsForRemovedInput, + mScheduledRecordings, new Filter<ScheduledRecording>() { @Override public boolean filter(ScheduledRecording r) { @@ -821,7 +859,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } }); List<RecordedProgram> movedRecordedPrograms = - moveElements(mRecordedProgramsForRemovedInput, mRecordedPrograms, + moveElements( + mRecordedProgramsForRemovedInput, + mRecordedPrograms, new Filter<RecordedProgram>() { @Override public boolean filter(RecordedProgram r) { @@ -830,7 +870,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { }); List<SeriesRecording> removedSeriesRecordings = new ArrayList<>(); List<SeriesRecording> movedSeriesRecordings = - moveElements(mSeriesRecordingsForRemovedInput, mSeriesRecordings, + moveElements( + mSeriesRecordingsForRemovedInput, + mSeriesRecordings, new Filter<SeriesRecording>() { @Override public boolean filter(SeriesRecording r) { @@ -856,8 +898,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { for (SeriesRecording r : removedSeriesRecordings) { mSeriesRecordingsForRemovedInput.remove(r.getId()); } - new AsyncDeleteSeriesRecordingTask(mContext).executeOnDbThread( - SeriesRecording.toArray(removedSeriesRecordings)); + new AsyncDeleteSeriesRecordingTask(mContext) + .executeOnDbThread(SeriesRecording.toArray(removedSeriesRecordings)); // Notify after all the data are moved. if (!movedSchedules.isEmpty()) { notifyScheduledRecordingAdded(ScheduledRecording.toArray(movedSchedules)); @@ -873,7 +915,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { private void hideInput(String inputId) { if (DEBUG) Log.d(TAG, "hideInput " + inputId); List<ScheduledRecording> movedSchedules = - moveElements(mScheduledRecordings, mScheduledRecordingsForRemovedInput, + moveElements( + mScheduledRecordings, + mScheduledRecordingsForRemovedInput, new Filter<ScheduledRecording>() { @Override public boolean filter(ScheduledRecording r) { @@ -881,7 +925,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } }); List<SeriesRecording> movedSeriesRecordings = - moveElements(mSeriesRecordings, mSeriesRecordingsForRemovedInput, + moveElements( + mSeriesRecordings, + mSeriesRecordingsForRemovedInput, new Filter<SeriesRecording>() { @Override public boolean filter(SeriesRecording r) { @@ -889,7 +935,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } }); List<RecordedProgram> movedRecordedPrograms = - moveElements(mRecordedPrograms, mRecordedProgramsForRemovedInput, + moveElements( + mRecordedPrograms, + mRecordedProgramsForRemovedInput, new Filter<RecordedProgram>() { @Override public boolean filter(RecordedProgram r) { @@ -931,7 +979,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { public void forgetStorage(String inputId) { List<ScheduledRecording> schedulesToDelete = new ArrayList<>(); for (Iterator<ScheduledRecording> i = - mScheduledRecordingsForRemovedInput.values().iterator(); i.hasNext(); ) { + mScheduledRecordingsForRemovedInput.values().iterator(); + i.hasNext(); ) { ScheduledRecording r = i.next(); if (inputId.equals(r.getInputId())) { schedulesToDelete.add(r); @@ -939,32 +988,34 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { } } List<SeriesRecording> seriesRecordingsToDelete = new ArrayList<>(); - for (Iterator<SeriesRecording> i = - mSeriesRecordingsForRemovedInput.values().iterator(); i.hasNext(); ) { + for (Iterator<SeriesRecording> i = mSeriesRecordingsForRemovedInput.values().iterator(); + i.hasNext(); ) { SeriesRecording r = i.next(); if (inputId.equals(r.getInputId())) { seriesRecordingsToDelete.add(r); i.remove(); } } - for (Iterator<RecordedProgram> i = - mRecordedProgramsForRemovedInput.values().iterator(); i.hasNext(); ) { + for (Iterator<RecordedProgram> i = mRecordedProgramsForRemovedInput.values().iterator(); + i.hasNext(); ) { if (inputId.equals(i.next().getInputId())) { i.remove(); } } - new AsyncDeleteScheduleTask(mContext).executeOnDbThread( - ScheduledRecording.toArray(schedulesToDelete)); - new AsyncDeleteSeriesRecordingTask(mContext).executeOnDbThread( - SeriesRecording.toArray(seriesRecordingsToDelete)); - new AsyncDbTask<Void, Void, Void>() { + new AsyncDeleteScheduleTask(mContext) + .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete)); + new AsyncDeleteSeriesRecordingTask(mContext) + .executeOnDbThread(SeriesRecording.toArray(seriesRecordingsToDelete)); + new AsyncDbTask<Void, Void, Void>(mDbExecutor) { @Override protected Void doInBackground(Void... params) { ContentResolver resolver = mContext.getContentResolver(); - String args[] = { inputId }; + String[] args = {inputId}; try { - resolver.delete(RecordedPrograms.CONTENT_URI, - RecordedPrograms.COLUMN_INPUT_ID + " = ?", args); + resolver.delete( + RecordedPrograms.CONTENT_URI, + RecordedPrograms.COLUMN_INPUT_ID + " = ?", + args); } catch (SQLiteException e) { Log.e(TAG, "Failed to delete recorded programs for inputId: " + inputId, e); } @@ -996,7 +1047,7 @@ public class DvrDataManagerImpl extends BaseDvrDataManager { private final Uri mUri; public RecordedProgramsQueryTask(ContentResolver contentResolver, Uri uri) { - super(contentResolver, uri == null ? RecordedPrograms.CONTENT_URI : uri); + super(mDbExecutor, contentResolver, uri == null ? RecordedPrograms.CONTENT_URI : uri); mUri = uri; } diff --git a/src/com/android/tv/dvr/DvrManager.java b/src/com/android/tv/dvr/DvrManager.java index d222003d..63a245a3 100644 --- a/src/com/android/tv/dvr/DvrManager.java +++ b/src/com/android/tv/dvr/DvrManager.java @@ -36,13 +36,12 @@ import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.util.Log; import android.util.Range; - -import com.android.tv.ApplicationSingletons; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.common.feature.CommonFeatures; -import com.android.tv.data.Channel; +import com.android.tv.common.util.CommonUtils; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrDataManager.OnRecordedProgramLoadFinishedListener; import com.android.tv.dvr.DvrDataManager.RecordedProgramListener; import com.android.tv.dvr.DvrScheduleManager.OnInitializeListener; @@ -51,7 +50,6 @@ import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.util.AsyncDbTask; import com.android.tv.util.Utils; - import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -60,6 +58,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.Executor; /** * DVR manager class to add and remove recordings. UI can modify recording list through this class, @@ -76,13 +75,15 @@ public class DvrManager { // @GuardedBy("mListener") private final Map<Listener, Handler> mListener = new HashMap<>(); private final Context mAppContext; + private final Executor mDbExecutor; public DvrManager(Context context) { SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG); mAppContext = context.getApplicationContext(); - ApplicationSingletons appSingletons = TvApplication.getSingletons(context); - mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager(); - mScheduleManager = appSingletons.getDvrScheduleManager(); + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + mDbExecutor = tvSingletons.getDbExecutor(); + mDataManager = (WritableDvrDataManager) tvSingletons.getDvrDataManager(); + mScheduleManager = tvSingletons.getDvrScheduleManager(); if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) { createSeriesRecordingsForRecordedProgramsIfNeeded(mDataManager.getRecordedPrograms()); } else { @@ -103,37 +104,41 @@ public class DvrManager { }); } if (!mScheduleManager.isInitialized()) { - mScheduleManager.addOnInitializeListener(new OnInitializeListener() { + mScheduleManager.addOnInitializeListener( + new OnInitializeListener() { + @Override + public void onInitialize() { + mScheduleManager.removeOnInitializeListener(this); + if (mDataManager.isInitialized() + && mScheduleManager.isInitialized()) { + createSeriesRecordingsForRecordedProgramsIfNeeded( + mDataManager.getRecordedPrograms()); + } + } + }); + } + } + mDataManager.addRecordedProgramListener( + new RecordedProgramListener() { @Override - public void onInitialize() { - mScheduleManager.removeOnInitializeListener(this); - if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) { - createSeriesRecordingsForRecordedProgramsIfNeeded( - mDataManager.getRecordedPrograms()); + public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { + if (!mDataManager.isInitialized() || !mScheduleManager.isInitialized()) { + return; + } + for (RecordedProgram recordedProgram : recordedPrograms) { + createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram); } } - }); - } - } - mDataManager.addRecordedProgramListener(new RecordedProgramListener() { - @Override - public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { - if (!mDataManager.isInitialized() || !mScheduleManager.isInitialized()) { - return; - } - for (RecordedProgram recordedProgram : recordedPrograms) { - createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram); - } - } - @Override - public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { } + @Override + public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {} - @Override - public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { - // Removing series recording is handled in the SeriesRecordingDetailsFragment. - } - }); + @Override + public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { + // Removing series recording is handled in the + // SeriesRecordingDetailsFragment. + } + }); } private void createSeriesRecordingsForRecordedProgramsIfNeeded( @@ -153,33 +158,38 @@ public class DvrManager { } } - /** - * Schedules a recording for {@code program}. - */ + /** Schedules a recording for {@code program}. */ public ScheduledRecording addSchedule(Program program) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return null; } SeriesRecording seriesRecording = getSeriesRecording(program); - return addSchedule(program, seriesRecording == null - ? mScheduleManager.suggestNewPriority() - : seriesRecording.getPriority()); + return addSchedule( + program, + seriesRecording == null + ? mScheduleManager.suggestNewPriority() + : seriesRecording.getPriority()); } /** - * Schedules a recording for {@code program} with the highest priority so that the schedule - * can be recorded. + * Schedules a recording for {@code program} with the highest priority so that the schedule can + * be recorded. */ public ScheduledRecording addScheduleWithHighestPriority(Program program) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return null; } SeriesRecording seriesRecording = getSeriesRecording(program); - return addSchedule(program, seriesRecording == null - ? mScheduleManager.suggestNewPriority() - : mScheduleManager.suggestHighestPriority(seriesRecording.getInputId(), - new Range(program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()), - seriesRecording.getPriority())); + return addSchedule( + program, + seriesRecording == null + ? mScheduleManager.suggestNewPriority() + : mScheduleManager.suggestHighestPriority( + seriesRecording.getInputId(), + new Range( + program.getStartTimeUtcMillis(), + program.getEndTimeUtcMillis()), + seriesRecording.getPriority())); } private ScheduledRecording addSchedule(Program program, long priority) { @@ -190,21 +200,28 @@ public class DvrManager { } ScheduledRecording schedule; SeriesRecording seriesRecording = getSeriesRecording(program); - schedule = createScheduledRecordingBuilder(input.getId(), program) - .setPriority(priority) - .setSeriesRecordingId(seriesRecording == null ? SeriesRecording.ID_NOT_SET - : seriesRecording.getId()) - .build(); + schedule = + createScheduledRecordingBuilder(input.getId(), program) + .setPriority(priority) + .setSeriesRecordingId( + seriesRecording == null + ? SeriesRecording.ID_NOT_SET + : seriesRecording.getId()) + .build(); mDataManager.addScheduledRecording(schedule); return schedule; } - /** - * Adds a recording schedule with a time range. - */ + /** Adds a recording schedule with a time range. */ public void addSchedule(Channel channel, long startTime, long endTime) { - Log.i(TAG, "Adding scheduled recording of channel " + channel + " starting at " + - Utils.toTimeString(startTime) + " and ending at " + Utils.toTimeString(endTime)); + Log.i( + TAG, + "Adding scheduled recording of channel " + + channel + + " starting at " + + Utils.toTimeString(startTime) + + " and ending at " + + Utils.toTimeString(endTime)); if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return; } @@ -216,9 +233,7 @@ public class DvrManager { addScheduleInternal(input.getId(), channel.getId(), startTime, endTime); } - /** - * Adds the schedule. - */ + /** Adds the schedule. */ public void addSchedule(ScheduledRecording schedule) { if (mDataManager.isDvrScheduleLoadFinished()) { mDataManager.addScheduledRecording(schedule); @@ -226,19 +241,23 @@ public class DvrManager { } private void addScheduleInternal(String inputId, long channelId, long startTime, long endTime) { - mDataManager.addScheduledRecording(ScheduledRecording - .builder(inputId, channelId, startTime, endTime) - .setPriority(mScheduleManager.suggestNewPriority()) - .build()); + mDataManager.addScheduledRecording( + ScheduledRecording.builder(inputId, channelId, startTime, endTime) + .setPriority(mScheduleManager.suggestNewPriority()) + .build()); } - /** - * Adds a new series recording and schedules for the programs with the initial state. - */ - public SeriesRecording addSeriesRecording(Program selectedProgram, - List<Program> programsToSchedule, @SeriesRecording.SeriesState int initialState) { - Log.i(TAG, "Adding series recording for program " + selectedProgram + ", and schedules: " - + programsToSchedule); + /** Adds a new series recording and schedules for the programs with the initial state. */ + public SeriesRecording addSeriesRecording( + Program selectedProgram, + List<Program> programsToSchedule, + @SeriesRecording.SeriesState int initialState) { + Log.i( + TAG, + "Adding series recording for program " + + selectedProgram + + ", and schedules: " + + programsToSchedule); if (!SoftPreconditions.checkState(mDataManager.isInitialized())) { return null; } @@ -247,10 +266,11 @@ public class DvrManager { Log.e(TAG, "Can't find input for program: " + selectedProgram); return null; } - SeriesRecording seriesRecording = SeriesRecording.builder(input.getId(), selectedProgram) - .setPriority(mScheduleManager.suggestNewSeriesPriority()) - .setState(initialState) - .build(); + SeriesRecording seriesRecording = + SeriesRecording.builder(input.getId(), selectedProgram) + .setPriority(mScheduleManager.suggestNewSeriesPriority()) + .setState(initialState) + .build(); mDataManager.addSeriesRecording(seriesRecording); // The schedules for the recorded programs should be added not to create the schedule the // duplicate episodes. @@ -279,9 +299,11 @@ public class DvrManager { // Duplicate schedules can exist, but they will be deleted in a few days. And it's // also guaranteed that the schedules don't belong to any series recordings because // there are no more than one series recordings which have the same program title. - toAdd.add(ScheduledRecording.builder(recordedProgram) - .setPriority(series.getPriority()) - .setSeriesRecordingId(series.getId()).build()); + toAdd.add( + ScheduledRecording.builder(recordedProgram) + .setPriority(series.getPriority()) + .setSeriesRecordingId(series.getId()) + .build()); } } if (!toAdd.isEmpty()) { @@ -291,11 +313,11 @@ public class DvrManager { /** * Adds {@link ScheduledRecording}s for the series recording. - * <p> - * This method doesn't add the series recording. + * + * <p>This method doesn't add the series recording. */ - public void addScheduleToSeriesRecording(SeriesRecording series, - List<Program> programsToSchedule) { + public void addScheduleToSeriesRecording( + SeriesRecording series, List<Program> programsToSchedule) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return; } @@ -311,18 +333,20 @@ public class DvrManager { mDataManager.getScheduledRecordingForProgramId(program.getId()); if (scheduleWithSameProgram != null) { if (scheduleWithSameProgram.isNotStarted()) { - ScheduledRecording r = ScheduledRecording.buildFrom(scheduleWithSameProgram) - .setSeriesRecordingId(series.getId()) - .build(); + ScheduledRecording r = + ScheduledRecording.buildFrom(scheduleWithSameProgram) + .setSeriesRecordingId(series.getId()) + .build(); if (!r.equals(scheduleWithSameProgram)) { toUpdate.add(r); } } } else { - toAdd.add(createScheduledRecordingBuilder(input.getId(), program) - .setPriority(series.getPriority()) - .setSeriesRecordingId(series.getId()) - .build()); + toAdd.add( + createScheduledRecordingBuilder(input.getId(), program) + .setPriority(series.getPriority()) + .setSeriesRecordingId(series.getId()) + .build()); } } if (!toAdd.isEmpty()) { @@ -333,9 +357,7 @@ public class DvrManager { } } - /** - * Updates the series recording. - */ + /** Updates the series recording. */ public void updateSeriesRecording(SeriesRecording series) { if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { SeriesRecording previousSeries = mDataManager.getSeriesRecording(series.getId()); @@ -344,7 +366,7 @@ public class DvrManager { // schedules will be added by SeriesRecordingScheduler or by SeriesSettingsFragment. if (previousSeries.getChannelOption() != series.getChannelOption() || (previousSeries.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE - && previousSeries.getChannelId() != series.getChannelId())) { + && previousSeries.getChannelId() != series.getChannelId())) { List<ScheduledRecording> schedules = mDataManager.getScheduledRecordings(series.getId()); List<ScheduledRecording> schedulesToRemove = new ArrayList<>(); @@ -365,20 +387,21 @@ public class DvrManager { schedulesToRemove.add(deletedSchedule); } } - mDataManager.removeScheduledRecording(true, - ScheduledRecording.toArray(schedulesToRemove)); + mDataManager.removeScheduledRecording( + true, ScheduledRecording.toArray(schedulesToRemove)); } } mDataManager.updateSeriesRecording(series); - if (previousSeries == null - || previousSeries.getPriority() != series.getPriority()) { + if (previousSeries == null || previousSeries.getPriority() != series.getPriority()) { long priority = series.getPriority(); List<ScheduledRecording> schedulesToUpdate = new ArrayList<>(); - for (ScheduledRecording schedule - : mDataManager.getScheduledRecordings(series.getId())) { + for (ScheduledRecording schedule : + mDataManager.getScheduledRecordings(series.getId())) { if (schedule.isNotStarted() || schedule.isInProgress()) { - schedulesToUpdate.add(ScheduledRecording.buildFrom(schedule) - .setPriority(priority).build()); + schedulesToUpdate.add( + ScheduledRecording.buildFrom(schedule) + .setPriority(priority) + .build()); } } if (!schedulesToUpdate.isEmpty()) { @@ -411,28 +434,26 @@ public class DvrManager { mDataManager.removeSeriesRecording(series); } - /** - * Stops the currently recorded program - */ + /** Stops the currently recorded program */ public void stopRecording(final ScheduledRecording recording) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return; } synchronized (mListener) { for (final Entry<Listener, Handler> entry : mListener.entrySet()) { - entry.getValue().post(new Runnable() { - @Override - public void run() { - entry.getKey().onStopRecordingRequested(recording); - } - }); + entry.getValue() + .post( + new Runnable() { + @Override + public void run() { + entry.getKey().onStopRecordingRequested(recording); + } + }); } } } - /** - * Removes scheduled recordings or an existing recordings. - */ + /** Removes scheduled recordings or an existing recordings. */ public void removeScheduledRecording(ScheduledRecording... schedules) { Log.i(TAG, "Removing " + Arrays.asList(schedules)); if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { @@ -447,9 +468,7 @@ public class DvrManager { } } - /** - * Removes scheduled recordings without changing to the DELETED state. - */ + /** Removes scheduled recordings without changing to the DELETED state. */ public void forceRemoveScheduledRecording(ScheduledRecording... schedules) { Log.i(TAG, "Force removing " + Arrays.asList(schedules)); if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { @@ -464,9 +483,7 @@ public class DvrManager { } } - /** - * Removes the recorded program. It deletes the file if possible. - */ + /** Removes the recorded program. It deletes the file if possible. */ public void removeRecordedProgram(Uri recordedProgramUri) { if (!SoftPreconditions.checkState(mDataManager.isInitialized())) { return; @@ -474,9 +491,7 @@ public class DvrManager { removeRecordedProgram(ContentUris.parseId(recordedProgramUri)); } - /** - * Removes the recorded program. It deletes the file if possible. - */ + /** Removes the recorded program. It deletes the file if possible. */ public void removeRecordedProgram(long recordedProgramId) { if (!SoftPreconditions.checkState(mDataManager.isInitialized())) { return; @@ -487,14 +502,12 @@ public class DvrManager { } } - /** - * Removes the recorded program. It deletes the file if possible. - */ + /** Removes the recorded program. It deletes the file if possible. */ public void removeRecordedProgram(final RecordedProgram recordedProgram) { if (!SoftPreconditions.checkState(mDataManager.isInitialized())) { return; } - new AsyncDbTask<Void, Void, Integer>() { + new AsyncDbTask<Void, Void, Integer>(mDbExecutor) { @Override protected Integer doInBackground(Void... params) { ContentResolver resolver = mAppContext.getContentResolver(); @@ -526,7 +539,7 @@ public class DvrManager { dbOperations.add(ContentProviderOperation.newDelete(r.getUri()).build()); } } - new AsyncDbTask<Void, Void, Boolean>() { + new AsyncDbTask<Void, Void, Boolean>(mDbExecutor) { @Override protected Boolean doInBackground(Void... params) { ContentResolver resolver = mAppContext.getContentResolver(); @@ -556,9 +569,7 @@ public class DvrManager { }.executeOnDbThread(); } - /** - * Updates the scheduled recording. - */ + /** Updates the scheduled recording. */ public void updateScheduledRecording(ScheduledRecording recording) { if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { mDataManager.updateScheduledRecording(recording); @@ -566,8 +577,8 @@ public class DvrManager { } /** - * Returns priority ordered list of all scheduled recordings that will not be recorded if - * this program is. + * Returns priority ordered list of all scheduled recordings that will not be recorded if this + * program is. * * @see DvrScheduleManager#getConflictingSchedules(Program) */ @@ -579,13 +590,13 @@ public class DvrManager { } /** - * Returns priority ordered list of all scheduled recordings that will not be recorded if - * this channel is. + * Returns priority ordered list of all scheduled recordings that will not be recorded if this + * channel is. * * @see DvrScheduleManager#getConflictingSchedules(long, long, long) */ - public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs, - long endTimeMs) { + public List<ScheduledRecording> getConflictingSchedules( + long channelId, long startTimeMs, long endTimeMs) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return Collections.emptyList(); } @@ -595,8 +606,8 @@ public class DvrManager { /** * Checks if the schedule is conflicting. * - * <p>Note that the {@code schedule} should be the existing one. If not, this returns - * {@code false}. + * <p>Note that the {@code schedule} should be the existing one. If not, this returns {@code + * false}. */ public boolean isConflicting(ScheduledRecording schedule) { return schedule != null @@ -605,8 +616,8 @@ public class DvrManager { } /** - * Returns priority ordered list of all scheduled recording that will not be recorded if - * this channel is tuned to. + * Returns priority ordered list of all scheduled recording that will not be recorded if this + * channel is tuned to. * * @see DvrScheduleManager#getConflictingSchedulesForTune */ @@ -617,22 +628,18 @@ public class DvrManager { return mScheduleManager.getConflictingSchedulesForTune(channelId); } - /** - * Sets the highest priority to the schedule. - */ + /** Sets the highest priority to the schedule. */ public void setHighestPriority(ScheduledRecording schedule) { if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { long newPriority = mScheduleManager.suggestHighestPriority(schedule); if (newPriority != schedule.getPriority()) { - mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule) - .setPriority(newPriority).build()); + mDataManager.updateScheduledRecording( + ScheduledRecording.buildFrom(schedule).setPriority(newPriority).build()); } } } - /** - * Suggests the higher priority than the schedules which overlap with {@code schedule}. - */ + /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */ public long suggestHighestPriority(ScheduledRecording schedule) { if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return mScheduleManager.suggestHighestPriority(schedule); @@ -642,9 +649,9 @@ public class DvrManager { /** * Returns {@code true} if the channel can be recorded. - * <p> - * Note that this method doesn't check the conflict of the schedule or available tuners. - * This can be called from the UI before the schedules are loaded. + * + * <p>Note that this method doesn't check the conflict of the schedule or available tuners. This + * can be called from the UI before the schedules are loaded. */ public boolean isChannelRecordable(Channel channel) { if (!mDataManager.isDvrScheduleLoadFinished() || channel == null) { @@ -661,23 +668,27 @@ public class DvrManager { if (!info.canRecord()) { return false; } - Program program = TvApplication.getSingletons(mAppContext).getProgramDataManager() - .getCurrentProgram(channel.getId()); + Program program = + TvSingletons.getSingletons(mAppContext) + .getProgramDataManager() + .getCurrentProgram(channel.getId()); return program == null || !program.isRecordingProhibited(); } /** * Returns {@code true} if the program can be recorded. - * <p> - * Note that this method doesn't check the conflict of the schedule or available tuners. - * This can be called from the UI before the schedules are loaded. + * + * <p>Note that this method doesn't check the conflict of the schedule or available tuners. This + * can be called from the UI before the schedules are loaded. */ public boolean isProgramRecordable(Program program) { if (!mDataManager.isInitialized()) { return false; } - Channel channel = TvApplication.getSingletons(mAppContext).getChannelDataManager() - .getChannel(program.getChannelId()); + Channel channel = + TvSingletons.getSingletons(mAppContext) + .getChannelDataManager() + .getChannel(program.getChannelId()); if (channel == null || channel.isRecordingProhibited()) { return false; } @@ -691,8 +702,8 @@ public class DvrManager { /** * Returns the current recording for the channel. - * <p> - * This can be called from the UI before the schedules are loaded. + * + * <p>This can be called from the UI before the schedules are loaded. */ public ScheduledRecording getCurrentRecording(long channelId) { if (!mDataManager.isDvrScheduleLoadFinished()) { @@ -707,8 +718,8 @@ public class DvrManager { } /** - * Returns schedules which is available (i.e., isNotStarted or isInProgress) and belongs to - * the series recording {@code seriesRecordingId}. + * Returns schedules which is available (i.e., isNotStarted or isInProgress) and belongs to the + * series recording {@code seriesRecordingId}. */ public List<ScheduledRecording> getAvailableScheduledRecording(long seriesRecordingId) { if (!mDataManager.isDvrScheduleLoadFinished()) { @@ -723,9 +734,7 @@ public class DvrManager { return schedules; } - /** - * Returns the series recording related to the program. - */ + /** Returns the series recording related to the program. */ @Nullable public SeriesRecording getSeriesRecording(Program program) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { @@ -735,8 +744,8 @@ public class DvrManager { } /** - * Returns if there are valid items. Valid item contains {@link RecordedProgram}, - * available {@link ScheduledRecording} and {@link SeriesRecording}. + * Returns if there are valid items. Valid item contains {@link RecordedProgram}, available + * {@link ScheduledRecording} and {@link SeriesRecording}. */ public boolean hasValidItems() { return !(mDataManager.getRecordedPrograms().isEmpty() @@ -768,8 +777,8 @@ public class DvrManager { * Returns ScheduledRecording.builder based on {@code program}. If program is already started, * recording started time is clipped to the current time. */ - private ScheduledRecording.Builder createScheduledRecordingBuilder(String inputId, - Program program) { + private ScheduledRecording.Builder createScheduledRecordingBuilder( + String inputId, Program program) { ScheduledRecording.Builder builder = ScheduledRecording.builder(inputId, program); long time = System.currentTimeMillis(); if (program.getStartTimeUtcMillis() < time && time < program.getEndTimeUtcMillis()) { @@ -778,13 +787,13 @@ public class DvrManager { return builder; } - /** - * Returns a schedule which matches to the given episode. - */ - public ScheduledRecording getScheduledRecording(String title, String seasonNumber, - String episodeNumber) { - if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null - || seasonNumber == null || episodeNumber == null) { + /** Returns a schedule which matches to the given episode. */ + public ScheduledRecording getScheduledRecording( + String title, String seasonNumber, String episodeNumber) { + if (!SoftPreconditions.checkState(mDataManager.isInitialized()) + || title == null + || seasonNumber == null + || episodeNumber == null) { return null; } for (ScheduledRecording r : mDataManager.getAllScheduledRecordings()) { @@ -797,13 +806,13 @@ public class DvrManager { return null; } - /** - * Returns a recorded program which is the same episode as the given {@code program}. - */ - public RecordedProgram getRecordedProgram(String title, String seasonNumber, - String episodeNumber) { - if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null - || seasonNumber == null || episodeNumber == null) { + /** Returns a recorded program which is the same episode as the given {@code program}. */ + public RecordedProgram getRecordedProgram( + String title, String seasonNumber, String episodeNumber) { + if (!SoftPreconditions.checkState(mDataManager.isInitialized()) + || title == null + || seasonNumber == null + || episodeNumber == null) { return null; } for (RecordedProgram r : mDataManager.getRecordedPrograms()) { @@ -820,13 +829,14 @@ public class DvrManager { @WorkerThread private void removeRecordedData(Uri dataUri) { try { - if (dataUri != null && ContentResolver.SCHEME_FILE.equals(dataUri.getScheme()) + if (dataUri != null + && ContentResolver.SCHEME_FILE.equals(dataUri.getScheme()) && dataUri.getPath() != null) { File recordedProgramPath = new File(dataUri.getPath()); if (!recordedProgramPath.exists()) { if (DEBUG) Log.d(TAG, "File to delete not exist: " + recordedProgramPath); } else { - Utils.deleteDirOrFile(recordedProgramPath); + CommonUtils.deleteDirOrFile(recordedProgramPath); if (DEBUG) { Log.d(TAG, "Sucessfully deleted files of the recorded program: " + dataUri); } @@ -834,16 +844,19 @@ public class DvrManager { } } catch (SecurityException e) { if (DEBUG) { - Log.d(TAG, "To delete this recorded program, please manually delete video data at" - + "\nadb shell rm -rf " + dataUri); + Log.d( + TAG, + "To delete this recorded program, please manually delete video data at" + + "\nadb shell rm -rf " + + dataUri); } } } /** * Remove all the records related to the input. - * <p> - * Note that this should be called after the input was removed. + * + * <p>Note that this should be called after the input was removed. */ public void forgetStorage(String inputId) { if (mDataManager.isInitialized()) { diff --git a/src/com/android/tv/dvr/DvrScheduleManager.java b/src/com/android/tv/dvr/DvrScheduleManager.java index b72117aa..d5126b12 100644 --- a/src/com/android/tv/dvr/DvrScheduleManager.java +++ b/src/com/android/tv/dvr/DvrScheduleManager.java @@ -25,21 +25,18 @@ import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import android.util.ArraySet; import android.util.Range; - -import com.android.tv.ApplicationSingletons; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; -import com.android.tv.dvr.recorder.InputTaskScheduler; -import com.android.tv.util.CompositeComparator; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; +import com.android.tv.dvr.recorder.InputTaskScheduler; +import com.android.tv.util.CompositeComparator; import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -50,21 +47,16 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; -/** - * A class to manage the schedules. - */ +/** A class to manage the schedules. */ @TargetApi(Build.VERSION_CODES.N) @MainThread +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated public class DvrScheduleManager { private static final String TAG = "DvrScheduleManager"; - /** - * The default priority of scheduled recording. - */ + /** The default priority of scheduled recording. */ public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1; - /** - * The default priority of series recording. - */ + /** The default priority of series recording. */ public static final long DEFAULT_SERIES_PRIORITY = DEFAULT_PRIORITY >> 1; // The new priority will have the offset from the existing one. private static final long PRIORITY_OFFSET = 1024; @@ -102,9 +94,9 @@ public class DvrScheduleManager { public DvrScheduleManager(Context context) { mContext = context; - ApplicationSingletons appSingletons = TvApplication.getSingletons(context); - mDataManager = (DvrDataManagerImpl) appSingletons.getDvrDataManager(); - mChannelDataManager = appSingletons.getChannelDataManager(); + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + mDataManager = (DvrDataManagerImpl) tvSingletons.getDvrDataManager(); + mChannelDataManager = tvSingletons.getChannelDataManager(); if (mDataManager.isDvrScheduleLoadFinished() && mChannelDataManager.isDbLoadFinished()) { buildData(); } else { @@ -119,146 +111,151 @@ public class DvrScheduleManager { } }); } - ScheduledRecordingListener scheduledRecordingListener = new ScheduledRecordingListener() { - @Override - public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { - if (!mInitialized) { - return; - } - for (ScheduledRecording schedule : scheduledRecordings) { - if (!schedule.isNotStarted() && !schedule.isInProgress()) { - continue; - } - TvInputInfo input = Utils - .getTvInputInfoForInputId(mContext, schedule.getInputId()); - if (!SoftPreconditions.checkArgument(input != null, TAG, - "Input was removed for : " + schedule)) { - // Input removed. - mInputScheduleMap.remove(schedule.getInputId()); - mInputConflictInfoMap.remove(schedule.getInputId()); - continue; - } - String inputId = input.getId(); - List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); - if (schedules == null) { - schedules = new ArrayList<>(); - mInputScheduleMap.put(inputId, schedules); + ScheduledRecordingListener scheduledRecordingListener = + new ScheduledRecordingListener() { + @Override + public void onScheduledRecordingAdded( + ScheduledRecording... scheduledRecordings) { + if (!mInitialized) { + return; + } + for (ScheduledRecording schedule : scheduledRecordings) { + if (!schedule.isNotStarted() && !schedule.isInProgress()) { + continue; + } + TvInputInfo input = + Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); + if (!SoftPreconditions.checkArgument( + input != null, TAG, "Input was removed for : %s", schedule)) { + // Input removed. + mInputScheduleMap.remove(schedule.getInputId()); + mInputConflictInfoMap.remove(schedule.getInputId()); + continue; + } + String inputId = input.getId(); + List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); + if (schedules == null) { + schedules = new ArrayList<>(); + mInputScheduleMap.put(inputId, schedules); + } + schedules.add(schedule); + } + onSchedulesChanged(); + notifyScheduledRecordingAdded(scheduledRecordings); } - schedules.add(schedule); - } - onSchedulesChanged(); - notifyScheduledRecordingAdded(scheduledRecordings); - } - @Override - public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { - if (!mInitialized) { - return; - } - for (ScheduledRecording schedule : scheduledRecordings) { - TvInputInfo input = Utils - .getTvInputInfoForInputId(mContext, schedule.getInputId()); - if (input == null) { - // Input removed. - mInputScheduleMap.remove(schedule.getInputId()); - mInputConflictInfoMap.remove(schedule.getInputId()); - continue; - } - String inputId = input.getId(); - List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); - if (schedules != null) { - schedules.remove(schedule); - if (schedules.isEmpty()) { - mInputScheduleMap.remove(inputId); + @Override + public void onScheduledRecordingRemoved( + ScheduledRecording... scheduledRecordings) { + if (!mInitialized) { + return; } - } - Map<Long, ConflictInfo> conflictInfo = mInputConflictInfoMap.get(inputId); - if (conflictInfo != null) { - conflictInfo.remove(schedule.getId()); - if (conflictInfo.isEmpty()) { - mInputConflictInfoMap.remove(inputId); + for (ScheduledRecording schedule : scheduledRecordings) { + TvInputInfo input = + Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); + if (input == null) { + // Input removed. + mInputScheduleMap.remove(schedule.getInputId()); + mInputConflictInfoMap.remove(schedule.getInputId()); + continue; + } + String inputId = input.getId(); + List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); + if (schedules != null) { + schedules.remove(schedule); + if (schedules.isEmpty()) { + mInputScheduleMap.remove(inputId); + } + } + Map<Long, ConflictInfo> conflictInfo = + mInputConflictInfoMap.get(inputId); + if (conflictInfo != null) { + conflictInfo.remove(schedule.getId()); + if (conflictInfo.isEmpty()) { + mInputConflictInfoMap.remove(inputId); + } + } } + onSchedulesChanged(); + notifyScheduledRecordingRemoved(scheduledRecordings); } - } - onSchedulesChanged(); - notifyScheduledRecordingRemoved(scheduledRecordings); - } - @Override - public void onScheduledRecordingStatusChanged( - ScheduledRecording... scheduledRecordings) { - if (!mInitialized) { - return; - } - for (ScheduledRecording schedule : scheduledRecordings) { - TvInputInfo input = Utils - .getTvInputInfoForInputId(mContext, schedule.getInputId()); - if (!SoftPreconditions.checkArgument(input != null, TAG, - "Input was removed for : " + schedule)) { - // Input removed. - mInputScheduleMap.remove(schedule.getInputId()); - mInputConflictInfoMap.remove(schedule.getInputId()); - continue; - } - String inputId = input.getId(); - List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); - if (schedules == null) { - schedules = new ArrayList<>(); - mInputScheduleMap.put(inputId, schedules); - } - // Compare ID because ScheduledRecording.equals() doesn't work if the state - // is changed. - for (Iterator<ScheduledRecording> i = schedules.iterator(); i.hasNext(); ) { - if (i.next().getId() == schedule.getId()) { - i.remove(); - break; + @Override + public void onScheduledRecordingStatusChanged( + ScheduledRecording... scheduledRecordings) { + if (!mInitialized) { + return; } - } - if (schedule.isNotStarted() || schedule.isInProgress()) { - schedules.add(schedule); - } - if (schedules.isEmpty()) { - mInputScheduleMap.remove(inputId); - } - // Update conflict list as well - Map<Long, ConflictInfo> conflictInfo = mInputConflictInfoMap.get(inputId); - if (conflictInfo != null) { - ConflictInfo oldConflictInfo = conflictInfo.get(schedule.getId()); - if (oldConflictInfo != null) { - oldConflictInfo.schedule = schedule; + for (ScheduledRecording schedule : scheduledRecordings) { + TvInputInfo input = + Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); + if (!SoftPreconditions.checkArgument( + input != null, TAG, "Input was removed for : %s", schedule)) { + // Input removed. + mInputScheduleMap.remove(schedule.getInputId()); + mInputConflictInfoMap.remove(schedule.getInputId()); + continue; + } + String inputId = input.getId(); + List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); + if (schedules == null) { + schedules = new ArrayList<>(); + mInputScheduleMap.put(inputId, schedules); + } + // Compare ID because ScheduledRecording.equals() doesn't work if the + // state + // is changed. + for (Iterator<ScheduledRecording> i = schedules.iterator(); + i.hasNext(); ) { + if (i.next().getId() == schedule.getId()) { + i.remove(); + break; + } + } + if (schedule.isNotStarted() || schedule.isInProgress()) { + schedules.add(schedule); + } + if (schedules.isEmpty()) { + mInputScheduleMap.remove(inputId); + } + // Update conflict list as well + Map<Long, ConflictInfo> conflictInfo = + mInputConflictInfoMap.get(inputId); + if (conflictInfo != null) { + ConflictInfo oldConflictInfo = conflictInfo.get(schedule.getId()); + if (oldConflictInfo != null) { + oldConflictInfo.schedule = schedule; + } + } } + onSchedulesChanged(); + notifyScheduledRecordingStatusChanged(scheduledRecordings); } - } - onSchedulesChanged(); - notifyScheduledRecordingStatusChanged(scheduledRecordings); - } - }; + }; mDataManager.addScheduledRecordingListener(scheduledRecordingListener); - ChannelDataManager.Listener channelDataManagerListener = new ChannelDataManager.Listener() { - @Override - public void onLoadFinished() { - if (mDataManager.isDvrScheduleLoadFinished() && !mInitialized) { - buildData(); - } - } + ChannelDataManager.Listener channelDataManagerListener = + new ChannelDataManager.Listener() { + @Override + public void onLoadFinished() { + if (mDataManager.isDvrScheduleLoadFinished() && !mInitialized) { + buildData(); + } + } - @Override - public void onChannelListUpdated() { - if (mDataManager.isDvrScheduleLoadFinished()) { - buildData(); - } - } + @Override + public void onChannelListUpdated() { + if (mDataManager.isDvrScheduleLoadFinished()) { + buildData(); + } + } - @Override - public void onChannelBrowsableChanged() { - } - }; + @Override + public void onChannelBrowsableChanged() {} + }; mChannelDataManager.addListener(channelDataManagerListener); } - /** - * Returns the started recordings for the given input. - */ + /** Returns the started recordings for the given input. */ private List<ScheduledRecording> getStartedRecordings(String inputId) { if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) { return Collections.emptyList(); @@ -337,39 +334,29 @@ public class DvrScheduleManager { } } - /** - * Returns {@code true} if this class has been initialized. - */ + /** Returns {@code true} if this class has been initialized. */ public boolean isInitialized() { return mInitialized; } - /** - * Adds a {@link ScheduledRecordingListener}. - */ + /** Adds a {@link ScheduledRecordingListener}. */ public final void addScheduledRecordingListener(ScheduledRecordingListener listener) { mScheduledRecordingListeners.add(listener); } - /** - * Removes a {@link ScheduledRecordingListener}. - */ + /** Removes a {@link ScheduledRecordingListener}. */ public final void removeScheduledRecordingListener(ScheduledRecordingListener listener) { mScheduledRecordingListeners.remove(listener); } - /** - * Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. - */ + /** Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. */ private void notifyScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { for (ScheduledRecordingListener l : mScheduledRecordingListeners) { l.onScheduledRecordingAdded(scheduledRecordings); } } - /** - * Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. - */ + /** Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. */ private void notifyScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { for (ScheduledRecordingListener l : mScheduledRecordingListeners) { l.onScheduledRecordingRemoved(scheduledRecordings); @@ -385,48 +372,36 @@ public class DvrScheduleManager { } } - /** - * Adds a {@link OnInitializeListener}. - */ + /** Adds a {@link OnInitializeListener}. */ public final void addOnInitializeListener(OnInitializeListener listener) { mOnInitializeListeners.add(listener); } - /** - * Removes a {@link OnInitializeListener}. - */ + /** Removes a {@link OnInitializeListener}. */ public final void removeOnInitializeListener(OnInitializeListener listener) { mOnInitializeListeners.remove(listener); } - /** - * Calls {@link OnInitializeListener#onInitialize} for each listener. - */ + /** Calls {@link OnInitializeListener#onInitialize} for each listener. */ private void notifyInitialize() { for (OnInitializeListener l : mOnInitializeListeners) { l.onInitialize(); } } - /** - * Adds a {@link OnConflictStateChangeListener}. - */ + /** Adds a {@link OnConflictStateChangeListener}. */ public final void addOnConflictStateChangeListener(OnConflictStateChangeListener listener) { mOnConflictStateChangeListeners.add(listener); } - /** - * Removes a {@link OnConflictStateChangeListener}. - */ + /** Removes a {@link OnConflictStateChangeListener}. */ public final void removeOnConflictStateChangeListener(OnConflictStateChangeListener listener) { mOnConflictStateChangeListeners.remove(listener); } - /** - * Calls {@link OnConflictStateChangeListener#onConflictStateChange} for each listener. - */ - private void notifyConflictStateChange(boolean conflict, - ScheduledRecording... scheduledRecordings) { + /** Calls {@link OnConflictStateChangeListener#onConflictStateChange} for each listener. */ + private void notifyConflictStateChange( + boolean conflict, ScheduledRecording... scheduledRecordings) { for (OnConflictStateChangeListener l : mOnConflictStateChangeListeners) { l.onConflictStateChange(conflict, scheduledRecordings); } @@ -434,8 +409,8 @@ public class DvrScheduleManager { /** * Returns the priority for the program if it is recorded. - * <p> - * The recording will have the higher priority than the existing ones. + * + * <p>The recording will have the higher priority than the existing ones. */ public long suggestNewPriority() { if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) { @@ -454,9 +429,7 @@ public class DvrScheduleManager { return highestPriority + PRIORITY_OFFSET; } - /** - * Suggests the higher priority than the schedules which overlap with {@code schedule}. - */ + /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */ public long suggestHighestPriority(ScheduledRecording schedule) { List<ScheduledRecording> schedules = mInputScheduleMap.get(schedule.getInputId()); if (schedules == null) { @@ -464,7 +437,8 @@ public class DvrScheduleManager { } long highestPriority = Long.MIN_VALUE; for (ScheduledRecording r : schedules) { - if (!r.equals(schedule) && r.isOverLapping(schedule) + if (!r.equals(schedule) + && r.isOverLapping(schedule) && r.getPriority() > highestPriority) { highestPriority = r.getPriority(); } @@ -475,9 +449,7 @@ public class DvrScheduleManager { return highestPriority + PRIORITY_OFFSET; } - /** - * Suggests the higher priority than the schedules which overlap with {@code schedule}. - */ + /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */ public long suggestHighestPriority(String inputId, Range<Long> peroid, long basePriority) { List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId); if (schedules == null) { @@ -497,8 +469,8 @@ public class DvrScheduleManager { /** * Returns the priority for a series recording. - * <p> - * The recording will have the higher priority than the existing series. + * + * <p>The recording will have the higher priority than the existing series. */ public long suggestNewSeriesPriority() { if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) { @@ -510,7 +482,7 @@ public class DvrScheduleManager { /** * Returns the priority for a series recording by order of series recording priority. * - * Higher order will have higher priority. + * <p>Higher order will have higher priority. */ public static long suggestSeriesPriority(int order) { return DEFAULT_SERIES_PRIORITY + order * PRIORITY_OFFSET; @@ -527,21 +499,23 @@ public class DvrScheduleManager { } /** - * Returns a sorted list of all scheduled recordings that will not be recorded if - * this program is going to be recorded, with their priorities in decending order. - * <p> - * An empty list means there is no conflicts. If there is conflict, a priority higher than + * Returns a sorted list of all scheduled recordings that will not be recorded if this program + * is going to be recorded, with their priorities in decending order. + * + * <p>An empty list means there is no conflicts. If there is conflict, a priority higher than * the first recording in the returned list should be assigned to the new schedule of this * program to guarantee the program would be completely recorded. */ public List<ScheduledRecording> getConflictingSchedules(Program program) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); - SoftPreconditions.checkState(Program.isValid(program), TAG, - "Program is invalid: " + program); SoftPreconditions.checkState( - program.getStartTimeUtcMillis() < program.getEndTimeUtcMillis(), TAG, + Program.isProgramValid(program), TAG, "Program is invalid: " + program); + SoftPreconditions.checkState( + program.getStartTimeUtcMillis() < program.getEndTimeUtcMillis(), + TAG, "Program duration is empty: " + program); - if (!mInitialized || !Program.isValid(program) + if (!mInitialized + || !Program.isProgramValid(program) || program.getStartTimeUtcMillis() >= program.getEndTimeUtcMillis()) { return Collections.emptyList(); } @@ -549,17 +523,19 @@ public class DvrScheduleManager { if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } - return getConflictingSchedules(input, Collections.singletonList( - ScheduledRecording.builder(input.getId(), program) - .setPriority(suggestHighestPriority()) - .build())); + return getConflictingSchedules( + input, + Collections.singletonList( + ScheduledRecording.builder(input.getId(), program) + .setPriority(suggestHighestPriority()) + .build())); } /** * Returns list of all conflicting scheduled recordings for the given {@code seriesRecording} * recording. - * <p> - * Any empty list means there is no conflicts. + * + * <p>Any empty list means there is no conflicts. */ public List<ScheduledRecording> getConflictingSchedules(SeriesRecording seriesRecording) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); @@ -571,8 +547,8 @@ public class DvrScheduleManager { if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } - List<ScheduledRecording> scheduledRecordingForSeries = mDataManager.getScheduledRecordings( - seriesRecording.getId()); + List<ScheduledRecording> scheduledRecordingForSeries = + mDataManager.getScheduledRecordings(seriesRecording.getId()); List<ScheduledRecording> availableScheduledRecordingForSeries = new ArrayList<>(); for (ScheduledRecording scheduledRecording : scheduledRecordingForSeries) { if (scheduledRecording.isNotStarted() || scheduledRecording.isInProgress()) { @@ -586,15 +562,15 @@ public class DvrScheduleManager { } /** - * Returns a sorted list of all scheduled recordings that will not be recorded if - * this channel is going to be recorded, with their priority in decending order. - * <p> - * An empty list means there is no conflicts. If there is conflict, a priority higher than + * Returns a sorted list of all scheduled recordings that will not be recorded if this channel + * is going to be recorded, with their priority in decending order. + * + * <p>An empty list means there is no conflicts. If there is conflict, a priority higher than * the first recording in the returned list should be assigned to the new schedule of this * channel to guarantee the channel would be completely recorded in the designated time range. */ - public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs, - long endTimeMs) { + public List<ScheduledRecording> getConflictingSchedules( + long channelId, long startTimeMs, long endTimeMs) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); SoftPreconditions.checkState(startTimeMs < endTimeMs, TAG, "Recording duration is empty."); @@ -605,10 +581,12 @@ public class DvrScheduleManager { if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } - return getConflictingSchedules(input, Collections.singletonList( - ScheduledRecording.builder(input.getId(), channelId, startTimeMs, endTimeMs) - .setPriority(suggestHighestPriority()) - .build())); + return getConflictingSchedules( + input, + Collections.singletonList( + ScheduledRecording.builder(input.getId(), channelId, startTimeMs, endTimeMs) + .setPriority(suggestHighestPriority()) + .build())); } /** @@ -633,14 +611,14 @@ public class DvrScheduleManager { /** * Checks if the schedule is conflicting. * - * <p>Note that the {@code schedule} should be the existing one. If not, this returns - * {@code false}. + * <p>Note that the {@code schedule} should be the existing one. If not, this returns {@code + * false}. */ public boolean isConflicting(ScheduledRecording schedule) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); - SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : " - + schedule.getChannelId()); + SoftPreconditions.checkState( + input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId()); if (!mInitialized || input == null) { return false; } @@ -651,15 +629,15 @@ public class DvrScheduleManager { /** * Checks if the schedule is partially conflicting, i.e., part of the scheduled program might be * recorded even if the priority of the schedule is not raised. - * <p> - * If the given schedule is not conflicting or is totally conflicting, i.e., cannot be recorded - * at all, this method returns {@code false} in both cases. + * + * <p>If the given schedule is not conflicting or is totally conflicting, i.e., cannot be + * recorded at all, this method returns {@code false} in both cases. */ public boolean isPartiallyConflicting(@NonNull ScheduledRecording schedule) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); - SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : " - + schedule.getChannelId()); + SoftPreconditions.checkState( + input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId()); if (!mInitialized || input == null) { return false; } @@ -672,27 +650,35 @@ public class DvrScheduleManager { } /** - * Returns priority ordered list of all scheduled recordings that will not be recorded if - * this channel is tuned to. + * Returns priority ordered list of all scheduled recordings that will not be recorded if this + * channel is tuned to. */ public List<ScheduledRecording> getConflictingSchedulesForTune(long channelId) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId); - SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: " - + channelId); + SoftPreconditions.checkState( + input != null, TAG, "Can't find input for channel ID: " + channelId); if (!mInitialized || channelId == Channel.INVALID_ID || input == null) { return Collections.emptyList(); } - return getConflictingSchedulesForTune(input.getId(), channelId, System.currentTimeMillis(), - suggestHighestPriority(), getStartedRecordings(input.getId()), + return getConflictingSchedulesForTune( + input.getId(), + channelId, + System.currentTimeMillis(), + suggestHighestPriority(), + getStartedRecordings(input.getId()), input.getTunerCount()); } @VisibleForTesting - public static List<ScheduledRecording> getConflictingSchedulesForTune(String inputId, - long channelId, long currentTimeMs, long newPriority, - List<ScheduledRecording> startedRecordings, int tunerCount) { + public static List<ScheduledRecording> getConflictingSchedulesForTune( + String inputId, + long channelId, + long currentTimeMs, + long newPriority, + List<ScheduledRecording> startedRecordings, + int tunerCount) { boolean channelFound = false; for (ScheduledRecording schedule : startedRecordings) { if (schedule.getChannelId() == channelId) { @@ -704,10 +690,10 @@ public class DvrScheduleManager { if (!channelFound) { // The current channel is not being recorded. schedules = new ArrayList<>(startedRecordings); - schedules.add(ScheduledRecording - .builder(inputId, channelId, currentTimeMs, currentTimeMs + 1) - .setPriority(newPriority) - .build()); + schedules.add( + ScheduledRecording.builder(inputId, channelId, currentTimeMs, currentTimeMs + 1) + .setPriority(newPriority) + .build()); } else { schedules = startedRecordings; } @@ -715,17 +701,17 @@ public class DvrScheduleManager { } /** - * Returns priority ordered list of all scheduled recordings that will not be recorded if - * the user keeps watching this channel. - * <p> - * Note that if the user keeps watching the channel, the channel can be recorded. + * Returns priority ordered list of all scheduled recordings that will not be recorded if the + * user keeps watching this channel. + * + * <p>Note that if the user keeps watching the channel, the channel can be recorded. */ public List<ScheduledRecording> getConflictingSchedulesForWatching(long channelId) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId); - SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: " - + channelId); + SoftPreconditions.checkState( + input != null, TAG, "Can't find input for channel ID: " + channelId); if (!mInitialized || channelId == Channel.INVALID_ID || input == null) { return Collections.emptyList(); } @@ -733,12 +719,17 @@ public class DvrScheduleManager { if (schedules == null || schedules.isEmpty()) { return Collections.emptyList(); } - return getConflictingSchedulesForWatching(input.getId(), channelId, - System.currentTimeMillis(), suggestNewPriority(), schedules, input.getTunerCount()); + return getConflictingSchedulesForWatching( + input.getId(), + channelId, + System.currentTimeMillis(), + suggestNewPriority(), + schedules, + input.getTunerCount()); } - private List<ScheduledRecording> getConflictingSchedules(TvInputInfo input, - List<ScheduledRecording> schedulesToAdd) { + private List<ScheduledRecording> getConflictingSchedules( + TvInputInfo input, List<ScheduledRecording> schedulesToAdd) { SoftPreconditions.checkNotNull(input); if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); @@ -751,9 +742,13 @@ public class DvrScheduleManager { } @VisibleForTesting - static List<ScheduledRecording> getConflictingSchedulesForWatching(String inputId, - long channelId, long currentTimeMs, long newPriority, - @NonNull List<ScheduledRecording> schedules, int tunerCount) { + static List<ScheduledRecording> getConflictingSchedulesForWatching( + String inputId, + long channelId, + long currentTimeMs, + long newPriority, + @NonNull List<ScheduledRecording> schedules, + int tunerCount) { List<ScheduledRecording> schedulesToCheck = new ArrayList<>(schedules); List<ScheduledRecording> schedulesSameChannel = new ArrayList<>(); for (ScheduledRecording schedule : schedules) { @@ -763,10 +758,10 @@ public class DvrScheduleManager { } } // Assume that the user will watch the current channel forever. - schedulesToCheck.add(ScheduledRecording - .builder(inputId, channelId, currentTimeMs, Long.MAX_VALUE) - .setPriority(newPriority) - .build()); + schedulesToCheck.add( + ScheduledRecording.builder(inputId, channelId, currentTimeMs, Long.MAX_VALUE) + .setPriority(newPriority) + .build()); List<ScheduledRecording> result = new ArrayList<>(); result.addAll(getConflictingSchedules(schedulesSameChannel, 1)); result.addAll(getConflictingSchedules(schedulesToCheck, tunerCount)); @@ -775,8 +770,10 @@ public class DvrScheduleManager { } @VisibleForTesting - static List<ScheduledRecording> getConflictingSchedules(List<ScheduledRecording> schedulesToAdd, - List<ScheduledRecording> currentSchedules, int tunerCount) { + static List<ScheduledRecording> getConflictingSchedules( + List<ScheduledRecording> schedulesToAdd, + List<ScheduledRecording> currentSchedules, + int tunerCount) { List<ScheduledRecording> schedulesToCheck = new ArrayList<>(currentSchedules); // When the duplicate schedule is to be added, remove the current duplicate recording. for (Iterator<ScheduledRecording> iter = schedulesToCheck.iterator(); iter.hasNext(); ) { @@ -805,9 +802,7 @@ public class DvrScheduleManager { return getConflictingSchedules(schedulesToCheck, tunerCount, ranges); } - /** - * Returns all conflicting scheduled recordings for the given schedules and count of tuner. - */ + /** Returns all conflicting scheduled recordings for the given schedules and count of tuner. */ public static List<ScheduledRecording> getConflictingSchedules( List<ScheduledRecording> schedules, int tunerCount) { return getConflictingSchedules(schedules, tunerCount, null); @@ -825,21 +820,21 @@ public class DvrScheduleManager { } @VisibleForTesting - static List<ConflictInfo> getConflictingSchedulesInfo(List<ScheduledRecording> schedules, - int tunerCount) { + static List<ConflictInfo> getConflictingSchedulesInfo( + List<ScheduledRecording> schedules, int tunerCount) { return getConflictingSchedulesInfo(schedules, tunerCount, null); } /** * This is the core method to calculate all the conflicting schedules (in given periods). - * <p> - * Note that this method will ignore duplicated schedules with a same hash code. (Please refer - * to {@link ScheduledRecording#hashCode}.) + * + * <p>Note that this method will ignore duplicated schedules with a same hash code. (Please + * refer to {@link ScheduledRecording#hashCode}.) * * @return A {@link HashMap} from {@link ScheduledRecording} to {@link Boolean}. The boolean - * value denotes if the scheduled recording is partially conflicting, i.e., is possible - * to be partially recorded under the given schedules and tuner count {@code true}, - * or not {@code false}. + * value denotes if the scheduled recording is partially conflicting, i.e., is possible to + * be partially recorded under the given schedules and tuner count {@code true}, or not + * {@code false}. */ private static List<ConflictInfo> getConflictingSchedulesInfo( List<ScheduledRecording> schedules, int tunerCount, List<Range<Long>> periods) { @@ -886,14 +881,19 @@ public class DvrScheduleManager { if (earliestEndTime < schedule.getEndTimeMs()) { // The schedule can starts when other recording ends even though it's // clipped. - ScheduledRecording modifiedSchedule = ScheduledRecording.buildFrom(schedule) - .setStartTimeMs(earliestEndTime).build(); + ScheduledRecording modifiedSchedule = + ScheduledRecording.buildFrom(schedule) + .setStartTimeMs(earliestEndTime) + .build(); ScheduledRecording originalSchedule = modified2OriginalSchedules.getOrDefault(schedule, schedule); modified2OriginalSchedules.put(modifiedSchedule, originalSchedule); - int insertPosition = Collections.binarySearch(schedulesToCheck, - modifiedSchedule, - ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); + int insertPosition = + Collections.binarySearch( + schedulesToCheck, + modifiedSchedule, + ScheduledRecording + .START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); if (insertPosition >= 0) { schedulesToCheck.add(insertPosition, modifiedSchedule); } else { @@ -921,17 +921,19 @@ public class DvrScheduleManager { } } List<ConflictInfo> result = new ArrayList<>(conflicts.values()); - Collections.sort(result, new Comparator<ConflictInfo>() { - @Override - public int compare(ConflictInfo lhs, ConflictInfo rhs) { - return RESULT_COMPARATOR.compare(lhs.schedule, rhs.schedule); - } - }); + Collections.sort( + result, + new Comparator<ConflictInfo>() { + @Override + public int compare(ConflictInfo lhs, ConflictInfo rhs) { + return RESULT_COMPARATOR.compare(lhs.schedule, rhs.schedule); + } + }); return result; } - private static void removeFinishedRecordings(List<ScheduledRecording> recordings, - long currentTimeMs) { + private static void removeFinishedRecordings( + List<ScheduledRecording> recordings, long currentTimeMs) { for (Iterator<ScheduledRecording> iter = recordings.iterator(); iter.hasNext(); ) { if (iter.next().getEndTimeMs() <= currentTimeMs) { iter.remove(); @@ -939,11 +941,9 @@ public class DvrScheduleManager { } } - /** - * @see InputTaskScheduler#getReplacableTask - */ - private static ScheduledRecording findReplaceableRecording(List<ScheduledRecording> recordings, - ScheduledRecording schedule) { + /** @see InputTaskScheduler#getReplacableTask */ + private static ScheduledRecording findReplaceableRecording( + List<ScheduledRecording> recordings, ScheduledRecording schedule) { // Returns the recording with the following priority. // 1. The recording with the lowest priority is returned. // 2. If the priorities are the same, the recording which finishes early is returned. @@ -980,30 +980,24 @@ public class DvrScheduleManager { } } - /** - * A listener which is notified the initialization of schedule manager. - */ + /** A listener which is notified the initialization of schedule manager. */ public interface OnInitializeListener { - /** - * Called when the schedule manager has been initialized. - */ + /** Called when the schedule manager has been initialized. */ void onInitialize(); } - /** - * A listener which is notified the conflict state change of the schedules. - */ + /** A listener which is notified the conflict state change of the schedules. */ public interface OnConflictStateChangeListener { /** * Called when the conflicting schedules change. - * <p> - * Note that this can be called before - * {@link ScheduledRecordingListener#onScheduledRecordingAdded} is called. + * + * <p>Note that this can be called before {@link + * ScheduledRecordingListener#onScheduledRecordingAdded} is called. * * @param conflict {@code true} if the {@code schedules} are the new conflicts, otherwise - * {@code false}. + * {@code false}. * @param schedules the schedules */ void onConflictStateChange(boolean conflict, ScheduledRecording... schedules); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/DvrStorageStatusManager.java b/src/com/android/tv/dvr/DvrStorageStatusManager.java index 2d41d732..ed8d6903 100644 --- a/src/com/android/tv/dvr/DvrStorageStatusManager.java +++ b/src/com/android/tv/dvr/DvrStorageStatusManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. @@ -11,291 +11,55 @@ * 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 + * limitations under the License. */ - package com.android.tv.dvr; -import android.content.BroadcastReceiver; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.OperationApplicationException; import android.database.Cursor; -import android.media.tv.TvContract; import android.media.tv.TvInputInfo; import android.net.Uri; import android.os.AsyncTask; -import android.os.Environment; -import android.os.Looper; import android.os.RemoteException; -import android.os.StatFs; -import android.support.annotation.AnyThread; -import android.support.annotation.IntDef; -import android.support.annotation.WorkerThread; +import android.support.media.tv.TvContractCompat; import android.util.Log; - -import com.android.tv.TvApplication; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.common.feature.CommonFeatures; -import com.android.tv.tuner.tvinput.TunerTvInputService; +import com.android.tv.TvSingletons; +import com.android.tv.common.recording.RecordingStorageStatusManager; +import com.android.tv.common.util.CommonUtils; import com.android.tv.util.TvInputManagerHelper; -import com.android.tv.util.Utils; - import java.io.File; -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -/** - * Signals DVR storage status change such as plugging/unplugging. - */ -public class DvrStorageStatusManager { +/** A class for extending TV app-specific function to {@link RecordingStorageStatusManager}. */ +public class DvrStorageStatusManager extends RecordingStorageStatusManager { private static final String TAG = "DvrStorageStatusManager"; - private static final boolean DEBUG = false; - - /** - * Minimum storage size to support DVR - */ - public static final long MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES = 50 * 1024 * 1024 * 1024L; // 50GB - private static final long MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES - = 10 * 1024 * 1024 * 1024L; // 10GB - private static final String RECORDING_DATA_SUB_PATH = "/recording"; - - private static final String[] PROJECTION = { - TvContract.RecordedPrograms._ID, - TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME, - TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI - }; - private final static int BATCH_OPERATION_COUNT = 100; - - @IntDef({STORAGE_STATUS_OK, STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL, - STORAGE_STATUS_FREE_SPACE_INSUFFICIENT, STORAGE_STATUS_MISSING}) - @Retention(RetentionPolicy.SOURCE) - public @interface StorageStatus { - } - - /** - * Current storage is OK to record a program. - */ - public static final int STORAGE_STATUS_OK = 0; - - /** - * Current storage's total capacity is smaller than DVR requirement. - */ - public static final int STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL = 1; - - /** - * Current storage's free space is insufficient to record programs. - */ - public static final int STORAGE_STATUS_FREE_SPACE_INSUFFICIENT = 2; - - /** - * Current storage is missing. - */ - public static final int STORAGE_STATUS_MISSING = 3; private final Context mContext; - private final Set<OnStorageMountChangedListener> mOnStorageMountChangedListeners = - new CopyOnWriteArraySet<>(); - private final boolean mRunningInMainProcess; - private MountedStorageStatus mMountedStorageStatus; - private boolean mStorageValid; private CleanUpDbTask mCleanUpDbTask; - private class MountedStorageStatus { - private final boolean mStorageMounted; - private final File mStorageMountedDir; - private final long mStorageMountedCapacity; - - private MountedStorageStatus(boolean mounted, File mountedDir, long capacity) { - mStorageMounted = mounted; - mStorageMountedDir = mountedDir; - mStorageMountedCapacity = capacity; - } - - private boolean isValidForDvr() { - return mStorageMounted && mStorageMountedCapacity >= MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof MountedStorageStatus)) { - return false; - } - MountedStorageStatus status = (MountedStorageStatus) other; - return mStorageMounted == status.mStorageMounted - && Objects.equals(mStorageMountedDir, status.mStorageMountedDir) - && mStorageMountedCapacity == status.mStorageMountedCapacity; - } - } - - public interface OnStorageMountChangedListener { - - /** - * Listener for DVR storage status change. - * - * @param storageMounted {@code true} when DVR possible storage is mounted, - * {@code false} otherwise. - */ - void onStorageMountChanged(boolean storageMounted); - } - - private final class StorageStatusBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - MountedStorageStatus result = getStorageStatusInternal(); - if (mMountedStorageStatus.equals(result)) { - return; - } - mMountedStorageStatus = result; - if (result.mStorageMounted && mRunningInMainProcess) { - // Cleans up DB in LC process. - // Tuner process is not always on. - if (mCleanUpDbTask != null) { - mCleanUpDbTask.cancel(true); - } - mCleanUpDbTask = new CleanUpDbTask(); - mCleanUpDbTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - boolean valid = result.isValidForDvr(); - if (valid == mStorageValid) { - return; - } - mStorageValid = valid; - for (OnStorageMountChangedListener l : mOnStorageMountChangedListeners) { - l.onStorageMountChanged(valid); - } - } - } + private static final String[] PROJECTION = { + TvContractCompat.RecordedPrograms._ID, + TvContractCompat.RecordedPrograms.COLUMN_PACKAGE_NAME, + TvContractCompat.RecordedPrograms.COLUMN_RECORDING_DATA_URI + }; + private static final int BATCH_OPERATION_COUNT = 100; - /** - * Creates DvrStorageStatusManager. - * - * @param context {@link Context} - */ - public DvrStorageStatusManager(final Context context, boolean runningInMainProcess) { + public DvrStorageStatusManager(Context context) { + super(context); mContext = context; - mRunningInMainProcess = runningInMainProcess; - mMountedStorageStatus = getStorageStatusInternal(); - mStorageValid = mMountedStorageStatus.isValidForDvr(); - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_MEDIA_MOUNTED); - filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); - filter.addAction(Intent.ACTION_MEDIA_EJECT); - filter.addAction(Intent.ACTION_MEDIA_REMOVED); - filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL); - filter.addDataScheme(ContentResolver.SCHEME_FILE); - mContext.registerReceiver(new StorageStatusBroadcastReceiver(), filter); } - /** - * Adds the listener for receiving storage status change. - * - * @param listener - */ - public void addListener(OnStorageMountChangedListener listener) { - mOnStorageMountChangedListeners.add(listener); - } - - /** - * Removes the current listener. - */ - public void removeListener(OnStorageMountChangedListener listener) { - mOnStorageMountChangedListeners.remove(listener); - } - - /** - * Returns true if a storage is mounted. - */ - public boolean isStorageMounted() { - return mMountedStorageStatus.mStorageMounted; - } - - /** - * Returns the path to DVR recording data directory. - * This can take for a while sometimes. - */ - @WorkerThread - public File getRecordingRootDataDirectory() { - SoftPreconditions.checkState(Looper.myLooper() != Looper.getMainLooper()); - if (mMountedStorageStatus.mStorageMountedDir == null) { - return null; - } - File root = mContext.getExternalFilesDir(null); - String rootPath; - try { - rootPath = root != null ? root.getCanonicalPath() : null; - } catch (IOException | SecurityException e) { - return null; - } - return rootPath == null ? null : new File(rootPath + RECORDING_DATA_SUB_PATH); - } - - /** - * Returns the current storage status for DVR recordings. - * - * @return {@link StorageStatus} - */ - @AnyThread - public @StorageStatus int getDvrStorageStatus() { - MountedStorageStatus status = mMountedStorageStatus; - if (status.mStorageMountedDir == null) { - return STORAGE_STATUS_MISSING; - } - if (CommonFeatures.FORCE_RECORDING_UNTIL_NO_SPACE.isEnabled(mContext)) { - return STORAGE_STATUS_OK; - } - if (status.mStorageMountedCapacity < MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES) { - return STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL; - } - try { - StatFs statFs = new StatFs(status.mStorageMountedDir.toString()); - if (statFs.getAvailableBytes() < MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES) { - return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT; - } - } catch (IllegalArgumentException e) { - // In rare cases, storage status change was not notified yet. - SoftPreconditions.checkState(false); - return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT; - } - return STORAGE_STATUS_OK; - } - - /** - * Returns whether the storage has sufficient storage. - * - * @return {@code true} when there is sufficient storage, {@code false} otherwise - */ - public boolean isStorageSufficient() { - return getDvrStorageStatus() == STORAGE_STATUS_OK; - } - - private MountedStorageStatus getStorageStatusInternal() { - boolean storageMounted = - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); - File storageMountedDir = storageMounted ? Environment.getExternalStorageDirectory() : null; - storageMounted = storageMounted && storageMountedDir != null; - long storageMountedCapacity = 0L; - if (storageMounted) { - try { - StatFs statFs = new StatFs(storageMountedDir.toString()); - storageMountedCapacity = statFs.getTotalBytes(); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Storage mount status was changed."); - storageMounted = false; - storageMountedDir = null; - } + @Override + protected void cleanUpDbIfNeeded() { + if (mCleanUpDbTask != null) { + mCleanUpDbTask.cancel(true); } - return new MountedStorageStatus( - storageMounted, storageMountedDir, storageMountedCapacity); + mCleanUpDbTask = new CleanUpDbTask(); + mCleanUpDbTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private class CleanUpDbTask extends AsyncTask<Void, Void, Boolean> { @@ -307,26 +71,29 @@ public class DvrStorageStatusManager { @Override protected Boolean doInBackground(Void... params) { - @DvrStorageStatusManager.StorageStatus int storageStatus = getDvrStorageStatus(); - if (storageStatus == DvrStorageStatusManager.STORAGE_STATUS_MISSING) { + @StorageStatus int storageStatus = getDvrStorageStatus(); + if (storageStatus == STORAGE_STATUS_MISSING) { return null; } - if (storageStatus == DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) { + if (storageStatus == STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) { return true; } List<ContentProviderOperation> ops = getDeleteOps(); if (ops == null || ops.isEmpty()) { return null; } - Log.i(TAG, "New device storage mounted. # of recordings to be forgotten : " - + ops.size()); - for (int i = 0 ; i < ops.size() && !isCancelled() ; i += BATCH_OPERATION_COUNT) { - int toIndex = (i + BATCH_OPERATION_COUNT) > ops.size() - ? ops.size() : (i + BATCH_OPERATION_COUNT); + Log.i( + TAG, + "New device storage mounted. # of recordings to be forgotten : " + ops.size()); + for (int i = 0; i < ops.size() && !isCancelled(); i += BATCH_OPERATION_COUNT) { + int toIndex = + (i + BATCH_OPERATION_COUNT) > ops.size() + ? ops.size() + : (i + BATCH_OPERATION_COUNT); ArrayList<ContentProviderOperation> batchOps = new ArrayList<>(ops.subList(i, toIndex)); try { - mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, batchOps); + mContext.getContentResolver().applyBatch(TvContractCompat.AUTHORITY, batchOps); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to clean up RecordedPrograms.", e); } @@ -337,16 +104,16 @@ public class DvrStorageStatusManager { @Override protected void onPostExecute(Boolean forgetStorage) { if (forgetStorage != null && forgetStorage == true) { - DvrManager dvrManager = TvApplication.getSingletons(mContext).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(mContext).getDvrManager(); TvInputManagerHelper tvInputManagerHelper = - TvApplication.getSingletons(mContext).getTvInputManagerHelper(); + TvSingletons.getSingletons(mContext).getTvInputManagerHelper(); List<TvInputInfo> tvInputInfoList = tvInputManagerHelper.getTvInputInfos(true, false); if (tvInputInfoList == null || tvInputInfoList.isEmpty()) { return; } for (TvInputInfo info : tvInputInfoList) { - if (Utils.isBundledInput(info.getId())) { + if (CommonUtils.isBundledInput(info.getId())) { dvrManager.forgetStorage(info.getId()); } } @@ -359,16 +126,19 @@ public class DvrStorageStatusManager { private List<ContentProviderOperation> getDeleteOps() { List<ContentProviderOperation> ops = new ArrayList<>(); - try (Cursor c = mContentResolver.query( - TvContract.RecordedPrograms.CONTENT_URI, PROJECTION, null, null, null)) { + try (Cursor c = + mContentResolver.query( + TvContractCompat.RecordedPrograms.CONTENT_URI, + PROJECTION, + null, + null, + null)) { if (c == null) { return null; } while (c.moveToNext()) { - @DvrStorageStatusManager.StorageStatus int storageStatus = - getDvrStorageStatus(); - if (isCancelled() - || storageStatus == DvrStorageStatusManager.STORAGE_STATUS_MISSING) { + @StorageStatus int storageStatus = getDvrStorageStatus(); + if (isCancelled() || storageStatus == STORAGE_STATUS_MISSING) { ops.clear(); break; } @@ -379,15 +149,19 @@ public class DvrStorageStatusManager { continue; } Uri dataUri = Uri.parse(dataUriString); - if (!Utils.isInBundledPackageSet(packageName) - || dataUri == null || dataUri.getPath() == null + if (!CommonUtils.isInBundledPackageSet(packageName) + || dataUri == null + || dataUri.getPath() == null || !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) { continue; } File recordedProgramDir = new File(dataUri.getPath()); if (!recordedProgramDir.exists()) { - ops.add(ContentProviderOperation.newDelete( - TvContract.buildRecordedProgramUri(Long.parseLong(id))).build()); + ops.add( + ContentProviderOperation.newDelete( + TvContractCompat.buildRecordedProgramUri( + Long.parseLong(id))) + .build()); } } return ops; diff --git a/src/com/android/tv/dvr/DvrWatchedPositionManager.java b/src/com/android/tv/dvr/DvrWatchedPositionManager.java index da6ddb1a..8616962f 100644 --- a/src/com/android/tv/dvr/DvrWatchedPositionManager.java +++ b/src/com/android/tv/dvr/DvrWatchedPositionManager.java @@ -20,10 +20,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.media.tv.TvInputManager; import android.support.annotation.IntDef; - -import com.android.tv.common.SharedPreferencesUtils; +import com.android.tv.common.util.SharedPreferencesUtils; import com.android.tv.dvr.data.RecordedProgram; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -33,8 +31,8 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** - * A class to manage DVR watched state. - * It will remember and provides previous watched position of DVR playback. + * A class to manage DVR watched state. It will remember and provides previous watched position of + * DVR playback. */ public class DvrWatchedPositionManager { private SharedPreferences mWatchedPositions; @@ -49,55 +47,46 @@ public class DvrWatchedPositionManager { @Retention(RetentionPolicy.SOURCE) @IntDef({DVR_WATCHED_STATUS_NEW, DVR_WATCHED_STATUS_WATCHING, DVR_WATCHED_STATUS_WATCHED}) public @interface DvrWatchedStatus {} - /** - * The status indicates the recorded program has not been watched at all. - */ + /** The status indicates the recorded program has not been watched at all. */ public static final int DVR_WATCHED_STATUS_NEW = 0; - /** - * The status indicates the recorded program is being watched. - */ + /** The status indicates the recorded program is being watched. */ public static final int DVR_WATCHED_STATUS_WATCHING = 1; - /** - * The status indicates the recorded program was completely watched. - */ + /** The status indicates the recorded program was completely watched. */ public static final int DVR_WATCHED_STATUS_WATCHED = 2; public DvrWatchedPositionManager(Context context) { - mWatchedPositions = context.getSharedPreferences( - SharedPreferencesUtils.SHARED_PREF_DVR_WATCHED_POSITION, Context.MODE_PRIVATE); + mWatchedPositions = + context.getSharedPreferences( + SharedPreferencesUtils.SHARED_PREF_DVR_WATCHED_POSITION, + Context.MODE_PRIVATE); } - /** - * Sets the watched position of the give program. - */ + /** Sets the watched position of the give program. */ public void setWatchedPosition(long recordedProgramId, long positionMs) { mWatchedPositions.edit().putLong(Long.toString(recordedProgramId), positionMs).apply(); notifyWatchedPositionChanged(recordedProgramId, positionMs); } - /** - * Gets the watched position of the give program. - */ + /** Gets the watched position of the give program. */ public long getWatchedPosition(long recordedProgramId) { - return mWatchedPositions.getLong(Long.toString(recordedProgramId), - TvInputManager.TIME_SHIFT_INVALID_TIME); + return mWatchedPositions.getLong( + Long.toString(recordedProgramId), TvInputManager.TIME_SHIFT_INVALID_TIME); } - @DvrWatchedStatus public int getWatchedStatus(RecordedProgram recordedProgram) { + @DvrWatchedStatus + public int getWatchedStatus(RecordedProgram recordedProgram) { long watchedPosition = getWatchedPosition(recordedProgram.getId()); if (watchedPosition == TvInputManager.TIME_SHIFT_INVALID_TIME) { return DVR_WATCHED_STATUS_NEW; - } else if (watchedPosition > recordedProgram - .getDurationMillis() * DVR_WATCHED_THRESHOLD_RATE) { + } else if (watchedPosition + > recordedProgram.getDurationMillis() * DVR_WATCHED_THRESHOLD_RATE) { return DVR_WATCHED_STATUS_WATCHED; } else { return DVR_WATCHED_STATUS_WATCHING; } } - /** - * Adds {@link WatchedPositionChangedListener}. - */ + /** Adds {@link WatchedPositionChangedListener}. */ public void addListener(WatchedPositionChangedListener listener, long recordedProgramId) { if (recordedProgramId == RecordedProgram.ID_NOT_SET) { return; @@ -110,18 +99,14 @@ public class DvrWatchedPositionManager { listenerSet.add(listener); } - /** - * Removes {@link WatchedPositionChangedListener}. - */ + /** Removes {@link WatchedPositionChangedListener}. */ public void removeListener(WatchedPositionChangedListener listener) { for (long recordedProgramId : new ArrayList<>(mListeners.keySet())) { removeListener(listener, recordedProgramId); } } - /** - * Removes {@link WatchedPositionChangedListener}. - */ + /** Removes {@link WatchedPositionChangedListener}. */ public void removeListener(WatchedPositionChangedListener listener, long recordedProgramId) { Set<WatchedPositionChangedListener> listenerSet = mListeners.get(recordedProgramId); if (listenerSet == null) { @@ -144,9 +129,7 @@ public class DvrWatchedPositionManager { } public interface WatchedPositionChangedListener { - /** - * Called when the watched position of some program is changed. - */ + /** Called when the watched position of some program is changed. */ void onWatchedPositionChanged(long recordedProgramId, long positionMs); } } diff --git a/src/com/android/tv/dvr/WritableDvrDataManager.java b/src/com/android/tv/dvr/WritableDvrDataManager.java index 129ba153..1b505e80 100644 --- a/src/com/android/tv/dvr/WritableDvrDataManager.java +++ b/src/com/android/tv/dvr/WritableDvrDataManager.java @@ -17,7 +17,6 @@ package com.android.tv.dvr; import android.support.annotation.MainThread; - import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.ScheduledRecording.RecordingState; import com.android.tv.dvr.data.SeriesRecording; @@ -30,19 +29,13 @@ import com.android.tv.dvr.data.SeriesRecording; */ @MainThread public interface WritableDvrDataManager extends DvrDataManager { - /** - * Adds new recordings. - */ + /** Adds new recordings. */ void addScheduledRecording(ScheduledRecording... scheduledRecordings); - /** - * Adds new series recordings. - */ + /** Adds new series recordings. */ void addSeriesRecording(SeriesRecording... seriesRecordings); - /** - * Removes recordings. - */ + /** Removes recordings. */ void removeScheduledRecording(ScheduledRecording... scheduledRecordings); /** @@ -51,30 +44,30 @@ public interface WritableDvrDataManager extends DvrDataManager { */ void removeScheduledRecording(boolean forceRemove, ScheduledRecording... scheduledRecordings); - /** - * Removes series recordings. - */ + /** Removes series recordings. */ void removeSeriesRecording(SeriesRecording... seasonSchedules); - /** - * Updates existing recordings. - */ + /** Updates existing recordings. */ void updateScheduledRecording(ScheduledRecording... scheduledRecordings); - /** - * Updates existing series recordings. - */ + /** Updates existing series recordings. */ void updateSeriesRecording(SeriesRecording... seriesRecordings); + /** Changes the state of the recording. */ + void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState); + /** * Changes the state of the recording. + * + * @param reason the reason of this change */ - void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState); + void changeState( + ScheduledRecording scheduledRecording, @RecordingState int newState, int reason); /** * Remove all the records related to the input. - * <p> - * Note that this should be called after the input was removed. + * + * <p>Note that this should be called after the input was removed. */ void forgetStorage(String inputId); } diff --git a/src/com/android/tv/dvr/data/IdGenerator.java b/src/com/android/tv/dvr/data/IdGenerator.java index 2ade1dad..496651ba 100644 --- a/src/com/android/tv/dvr/data/IdGenerator.java +++ b/src/com/android/tv/dvr/data/IdGenerator.java @@ -18,32 +18,22 @@ package com.android.tv.dvr.data; import java.util.concurrent.atomic.AtomicLong; -/** - * A class which generate the ID which increases sequentially. - */ +/** A class which generate the ID which increases sequentially. */ public class IdGenerator { - /** - * ID generator for the scheduled recording. - */ + /** ID generator for the scheduled recording. */ public static final IdGenerator SCHEDULED_RECORDING = new IdGenerator(); - /** - * ID generator for the series recording. - */ + /** ID generator for the series recording. */ public static final IdGenerator SERIES_RECORDING = new IdGenerator(); private final AtomicLong mMaxId = new AtomicLong(0); - /** - * Sets the new maximum ID. - */ + /** Sets the new maximum ID. */ public void setMaxId(long maxId) { mMaxId.set(maxId); } - /** - * Returns the new ID which is greater than the existing maximum ID by 1. - */ + /** Returns the new ID which is greater than the existing maximum ID by 1. */ public long newId() { return mMaxId.incrementAndGet(); } diff --git a/src/com/android/tv/dvr/data/RecordedProgram.java b/src/com/android/tv/dvr/data/RecordedProgram.java index 2e953a52..e1fbca8c 100644 --- a/src/com/android/tv/dvr/data/RecordedProgram.java +++ b/src/com/android/tv/dvr/data/RecordedProgram.java @@ -28,99 +28,97 @@ import android.net.Uri; import android.os.Build; import android.support.annotation.Nullable; import android.text.TextUtils; - import com.android.tv.common.R; import com.android.tv.common.TvContentRatingCache; +import com.android.tv.common.util.CommonUtils; import com.android.tv.data.BaseProgram; import com.android.tv.data.GenreItems; import com.android.tv.data.InternalDataUtils; -import com.android.tv.util.Utils; - import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Objects; import java.util.concurrent.TimeUnit; -/** - * Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}. - */ +/** Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}. */ @TargetApi(Build.VERSION_CODES.N) public class RecordedProgram extends BaseProgram { public static final int ID_NOT_SET = -1; - public final static String[] PROJECTION = { - // These are in exactly the order listed in RecordedPrograms - RecordedPrograms._ID, - RecordedPrograms.COLUMN_PACKAGE_NAME, - RecordedPrograms.COLUMN_INPUT_ID, - RecordedPrograms.COLUMN_CHANNEL_ID, - RecordedPrograms.COLUMN_TITLE, - RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER, - RecordedPrograms.COLUMN_SEASON_TITLE, - RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER, - RecordedPrograms.COLUMN_EPISODE_TITLE, - RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, - RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, - RecordedPrograms.COLUMN_BROADCAST_GENRE, - RecordedPrograms.COLUMN_CANONICAL_GENRE, - RecordedPrograms.COLUMN_SHORT_DESCRIPTION, - RecordedPrograms.COLUMN_LONG_DESCRIPTION, - RecordedPrograms.COLUMN_VIDEO_WIDTH, - RecordedPrograms.COLUMN_VIDEO_HEIGHT, - RecordedPrograms.COLUMN_AUDIO_LANGUAGE, - RecordedPrograms.COLUMN_CONTENT_RATING, - RecordedPrograms.COLUMN_POSTER_ART_URI, - RecordedPrograms.COLUMN_THUMBNAIL_URI, - RecordedPrograms.COLUMN_SEARCHABLE, - RecordedPrograms.COLUMN_RECORDING_DATA_URI, - RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, - RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, - RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, - RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, - RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, - RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, - RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, - RecordedPrograms.COLUMN_VERSION_NUMBER, - RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, + public static final String[] PROJECTION = { + // These are in exactly the order listed in RecordedPrograms + RecordedPrograms._ID, + RecordedPrograms.COLUMN_PACKAGE_NAME, + RecordedPrograms.COLUMN_INPUT_ID, + RecordedPrograms.COLUMN_CHANNEL_ID, + RecordedPrograms.COLUMN_TITLE, + RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER, + RecordedPrograms.COLUMN_SEASON_TITLE, + RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER, + RecordedPrograms.COLUMN_EPISODE_TITLE, + RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, + RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, + RecordedPrograms.COLUMN_BROADCAST_GENRE, + RecordedPrograms.COLUMN_CANONICAL_GENRE, + RecordedPrograms.COLUMN_SHORT_DESCRIPTION, + RecordedPrograms.COLUMN_LONG_DESCRIPTION, + RecordedPrograms.COLUMN_VIDEO_WIDTH, + RecordedPrograms.COLUMN_VIDEO_HEIGHT, + RecordedPrograms.COLUMN_AUDIO_LANGUAGE, + RecordedPrograms.COLUMN_CONTENT_RATING, + RecordedPrograms.COLUMN_POSTER_ART_URI, + RecordedPrograms.COLUMN_THUMBNAIL_URI, + RecordedPrograms.COLUMN_SEARCHABLE, + RecordedPrograms.COLUMN_RECORDING_DATA_URI, + RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, + RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, + RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, + RecordedPrograms.COLUMN_VERSION_NUMBER, + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, }; public static RecordedProgram fromCursor(Cursor cursor) { int index = 0; - Builder builder = builder() - .setId(cursor.getLong(index++)) - .setPackageName(cursor.getString(index++)) - .setInputId(cursor.getString(index++)) - .setChannelId(cursor.getLong(index++)) - .setTitle(cursor.getString(index++)) - .setSeasonNumber(cursor.getString(index++)) - .setSeasonTitle(cursor.getString(index++)) - .setEpisodeNumber(cursor.getString(index++)) - .setEpisodeTitle(cursor.getString(index++)) - .setStartTimeUtcMillis(cursor.getLong(index++)) - .setEndTimeUtcMillis(cursor.getLong(index++)) - .setBroadcastGenres(cursor.getString(index++)) - .setCanonicalGenres(cursor.getString(index++)) - .setShortDescription(cursor.getString(index++)) - .setLongDescription(cursor.getString(index++)) - .setVideoWidth(cursor.getInt(index++)) - .setVideoHeight(cursor.getInt(index++)) - .setAudioLanguage(cursor.getString(index++)) - .setContentRatings( - TvContentRatingCache.getInstance().getRatings(cursor.getString(index++))) - .setPosterArtUri(cursor.getString(index++)) - .setThumbnailUri(cursor.getString(index++)) - .setSearchable(cursor.getInt(index++) == 1) - .setDataUri(cursor.getString(index++)) - .setDataBytes(cursor.getLong(index++)) - .setDurationMillis(cursor.getLong(index++)) - .setExpireTimeUtcMillis(cursor.getLong(index++)) - .setInternalProviderFlag1(cursor.getInt(index++)) - .setInternalProviderFlag2(cursor.getInt(index++)) - .setInternalProviderFlag3(cursor.getInt(index++)) - .setInternalProviderFlag4(cursor.getInt(index++)) - .setVersionNumber(cursor.getInt(index++)); - if (Utils.isInBundledPackageSet(builder.mPackageName)) { + Builder builder = + builder() + .setId(cursor.getLong(index++)) + .setPackageName(cursor.getString(index++)) + .setInputId(cursor.getString(index++)) + .setChannelId(cursor.getLong(index++)) + .setTitle(cursor.getString(index++)) + .setSeasonNumber(cursor.getString(index++)) + .setSeasonTitle(cursor.getString(index++)) + .setEpisodeNumber(cursor.getString(index++)) + .setEpisodeTitle(cursor.getString(index++)) + .setStartTimeUtcMillis(cursor.getLong(index++)) + .setEndTimeUtcMillis(cursor.getLong(index++)) + .setBroadcastGenres(cursor.getString(index++)) + .setCanonicalGenres(cursor.getString(index++)) + .setShortDescription(cursor.getString(index++)) + .setLongDescription(cursor.getString(index++)) + .setVideoWidth(cursor.getInt(index++)) + .setVideoHeight(cursor.getInt(index++)) + .setAudioLanguage(cursor.getString(index++)) + .setContentRatings( + TvContentRatingCache.getInstance() + .getRatings(cursor.getString(index++))) + .setPosterArtUri(cursor.getString(index++)) + .setThumbnailUri(cursor.getString(index++)) + .setSearchable(cursor.getInt(index++) == 1) + .setDataUri(cursor.getString(index++)) + .setDataBytes(cursor.getLong(index++)) + .setDurationMillis(cursor.getLong(index++)) + .setExpireTimeUtcMillis(cursor.getLong(index++)) + .setInternalProviderFlag1(cursor.getInt(index++)) + .setInternalProviderFlag2(cursor.getInt(index++)) + .setInternalProviderFlag3(cursor.getInt(index++)) + .setInternalProviderFlag4(cursor.getInt(index++)) + .setVersionNumber(cursor.getInt(index++)); + if (CommonUtils.isInBundledPackageSet(builder.mPackageName)) { InternalDataUtils.deserializeInternalProviderData(cursor.getBlob(index), builder); } return builder.build(); @@ -138,12 +136,14 @@ public class RecordedProgram extends BaseProgram { values.put(RecordedPrograms.COLUMN_SEASON_TITLE, recordedProgram.mSeasonTitle); values.put(RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER, recordedProgram.mEpisodeNumber); values.put(RecordedPrograms.COLUMN_EPISODE_TITLE, recordedProgram.mTitle); - values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, - recordedProgram.mStartTimeUtcMillis); + values.put( + RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, recordedProgram.mStartTimeUtcMillis); values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, recordedProgram.mEndTimeUtcMillis); - values.put(RecordedPrograms.COLUMN_BROADCAST_GENRE, + values.put( + RecordedPrograms.COLUMN_BROADCAST_GENRE, safeEncode(recordedProgram.mBroadcastGenres)); - values.put(RecordedPrograms.COLUMN_CANONICAL_GENRE, + values.put( + RecordedPrograms.COLUMN_CANONICAL_GENRE, safeEncode(recordedProgram.mCanonicalGenres)); values.put(RecordedPrograms.COLUMN_SHORT_DESCRIPTION, recordedProgram.mShortDescription); values.put(RecordedPrograms.COLUMN_LONG_DESCRIPTION, recordedProgram.mLongDescription); @@ -158,33 +158,40 @@ public class RecordedProgram extends BaseProgram { values.put(RecordedPrograms.COLUMN_VIDEO_HEIGHT, recordedProgram.mVideoHeight); } values.put(RecordedPrograms.COLUMN_AUDIO_LANGUAGE, recordedProgram.mAudioLanguage); - values.put(RecordedPrograms.COLUMN_CONTENT_RATING, + values.put( + RecordedPrograms.COLUMN_CONTENT_RATING, TvContentRatingCache.contentRatingsToString(recordedProgram.mContentRatings)); values.put(RecordedPrograms.COLUMN_POSTER_ART_URI, recordedProgram.mPosterArtUri); values.put(RecordedPrograms.COLUMN_THUMBNAIL_URI, recordedProgram.mThumbnailUri); values.put(RecordedPrograms.COLUMN_SEARCHABLE, recordedProgram.mSearchable ? 1 : 0); - values.put(RecordedPrograms.COLUMN_RECORDING_DATA_URI, - safeToString(recordedProgram.mDataUri)); + values.put( + RecordedPrograms.COLUMN_RECORDING_DATA_URI, safeToString(recordedProgram.mDataUri)); values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, recordedProgram.mDataBytes); - values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, - recordedProgram.mDurationMillis); - values.put(RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, + values.put( + RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, recordedProgram.mDurationMillis); + values.put( + RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, recordedProgram.mExpireTimeUtcMillis); - values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, + values.put( + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, InternalDataUtils.serializeInternalProviderData(recordedProgram)); - values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, + values.put( + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, recordedProgram.mInternalProviderFlag1); - values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, + values.put( + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, recordedProgram.mInternalProviderFlag2); - values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, + values.put( + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, recordedProgram.mInternalProviderFlag3); - values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, + values.put( + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, recordedProgram.mInternalProviderFlag4); values.put(RecordedPrograms.COLUMN_VERSION_NUMBER, recordedProgram.mVersionNumber); return values; } - public static class Builder{ + public static class Builder { private long mId = ID_NOT_SET; private String mPackageName; private String mInputId; @@ -414,18 +421,45 @@ public class RecordedProgram extends BaseProgram { // If series ID is not set, generate it for the episodic program of other TV input. setSeriesId(BaseProgram.generateSeriesId(mPackageName, mTitle)); } - return new RecordedProgram(mId, mPackageName, mInputId, mChannelId, mTitle, mSeriesId, - mSeasonNumber, mSeasonTitle, mEpisodeNumber, mEpisodeTitle, mStartTimeUtcMillis, - mEndTimeUtcMillis, mBroadcastGenres, mCanonicalGenres, mShortDescription, - mLongDescription, mVideoWidth, mVideoHeight, mAudioLanguage, mContentRatings, - mPosterArtUri, mThumbnailUri, mSearchable, mDataUri, mDataBytes, - mDurationMillis, mExpireTimeUtcMillis, mInternalProviderFlag1, - mInternalProviderFlag2, mInternalProviderFlag3, mInternalProviderFlag4, + return new RecordedProgram( + mId, + mPackageName, + mInputId, + mChannelId, + mTitle, + mSeriesId, + mSeasonNumber, + mSeasonTitle, + mEpisodeNumber, + mEpisodeTitle, + mStartTimeUtcMillis, + mEndTimeUtcMillis, + mBroadcastGenres, + mCanonicalGenres, + mShortDescription, + mLongDescription, + mVideoWidth, + mVideoHeight, + mAudioLanguage, + mContentRatings, + mPosterArtUri, + mThumbnailUri, + mSearchable, + mDataUri, + mDataBytes, + mDurationMillis, + mExpireTimeUtcMillis, + mInternalProviderFlag1, + mInternalProviderFlag2, + mInternalProviderFlag3, + mInternalProviderFlag4, mVersionNumber); } } - public static Builder builder() { return new Builder(); } + public static Builder builder() { + return new Builder(); + } public static Builder buildFrom(RecordedProgram orig) { return builder() @@ -470,7 +504,7 @@ public class RecordedProgram extends BaseProgram { } return Long.compare(lhs.mId, rhs.mId); } - }; + }; private static final long CLIPPED_THRESHOLD_MS = TimeUnit.MINUTES.toMillis(5); @@ -507,15 +541,38 @@ public class RecordedProgram extends BaseProgram { private final int mInternalProviderFlag4; private final int mVersionNumber; - private RecordedProgram(long id, String packageName, String inputId, long channelId, - String title, String seriesId, String seasonNumber, String seasonTitle, - String episodeNumber, String episodeTitle, long startTimeUtcMillis, - long endTimeUtcMillis, String[] broadcastGenres, String[] canonicalGenres, - String shortDescription, String longDescription, int videoWidth, int videoHeight, - String audioLanguage, TvContentRating[] contentRatings, String posterArtUri, - String thumbnailUri, boolean searchable, Uri dataUri, long dataBytes, - long durationMillis, long expireTimeUtcMillis, int internalProviderFlag1, - int internalProviderFlag2, int internalProviderFlag3, int internalProviderFlag4, + private RecordedProgram( + long id, + String packageName, + String inputId, + long channelId, + String title, + String seriesId, + String seasonNumber, + String seasonTitle, + String episodeNumber, + String episodeTitle, + long startTimeUtcMillis, + long endTimeUtcMillis, + String[] broadcastGenres, + String[] canonicalGenres, + String shortDescription, + String longDescription, + int videoWidth, + int videoHeight, + String audioLanguage, + TvContentRating[] contentRatings, + String posterArtUri, + String thumbnailUri, + boolean searchable, + Uri dataUri, + long dataBytes, + long durationMillis, + long expireTimeUtcMillis, + int internalProviderFlag1, + int internalProviderFlag2, + int internalProviderFlag3, + int internalProviderFlag4, int versionNumber) { mId = id; mPackageName = packageName; @@ -564,9 +621,7 @@ public class RecordedProgram extends BaseProgram { return mCanonicalGenres; } - /** - * Returns array of canonical genre ID's for this recorded program. - */ + /** Returns array of canonical genre ID's for this recorded program. */ @Override public int[] getCanonicalGenreIds() { if (mCanonicalGenres == null) { @@ -623,11 +678,15 @@ public class RecordedProgram extends BaseProgram { if (!TextUtils.isEmpty(mEpisodeNumber)) { if (TextUtils.equals(mSeasonNumber, "0")) { // Do not show "S0: ". - return String.format(context.getResources().getString( - R.string.display_episode_number_format_no_season_number), mEpisodeNumber); + return String.format( + context.getResources() + .getString(R.string.display_episode_number_format_no_season_number), + mEpisodeNumber); } else { - return String.format(context.getResources().getString( - R.string.display_episode_number_format), mSeasonNumber, mEpisodeNumber); + return String.format( + context.getResources().getString(R.string.display_episode_number_format), + mSeasonNumber, + mEpisodeNumber); } } return null; @@ -734,9 +793,7 @@ public class RecordedProgram extends BaseProgram { return mVideoWidth; } - /** - * Checks whether the recording has been clipped or not. - */ + /** Checks whether the recording has been clipped or not. */ public boolean isClipped() { return mEndTimeUtcMillis - mStartTimeUtcMillis - mDurationMillis > CLIPPED_THRESHOLD_MS; } @@ -746,40 +803,38 @@ public class RecordedProgram extends BaseProgram { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RecordedProgram that = (RecordedProgram) o; - return Objects.equals(mId, that.mId) && - Objects.equals(mChannelId, that.mChannelId) && - Objects.equals(mSeriesId, that.mSeriesId) && - Objects.equals(mSeasonNumber, that.mSeasonNumber) && - Objects.equals(mSeasonTitle, that.mSeasonTitle) && - Objects.equals(mEpisodeNumber, that.mEpisodeNumber) && - Objects.equals(mStartTimeUtcMillis, that.mStartTimeUtcMillis) && - Objects.equals(mEndTimeUtcMillis, that.mEndTimeUtcMillis) && - Objects.equals(mVideoWidth, that.mVideoWidth) && - Objects.equals(mVideoHeight, that.mVideoHeight) && - Objects.equals(mSearchable, that.mSearchable) && - Objects.equals(mDataBytes, that.mDataBytes) && - Objects.equals(mDurationMillis, that.mDurationMillis) && - Objects.equals(mExpireTimeUtcMillis, that.mExpireTimeUtcMillis) && - Objects.equals(mInternalProviderFlag1, that.mInternalProviderFlag1) && - Objects.equals(mInternalProviderFlag2, that.mInternalProviderFlag2) && - Objects.equals(mInternalProviderFlag3, that.mInternalProviderFlag3) && - Objects.equals(mInternalProviderFlag4, that.mInternalProviderFlag4) && - Objects.equals(mVersionNumber, that.mVersionNumber) && - Objects.equals(mTitle, that.mTitle) && - Objects.equals(mEpisodeTitle, that.mEpisodeTitle) && - Arrays.equals(mBroadcastGenres, that.mBroadcastGenres) && - Arrays.equals(mCanonicalGenres, that.mCanonicalGenres) && - Objects.equals(mShortDescription, that.mShortDescription) && - Objects.equals(mLongDescription, that.mLongDescription) && - Objects.equals(mAudioLanguage, that.mAudioLanguage) && - Arrays.equals(mContentRatings, that.mContentRatings) && - Objects.equals(mPosterArtUri, that.mPosterArtUri) && - Objects.equals(mThumbnailUri, that.mThumbnailUri); - } - - /** - * Hashes based on the ID. - */ + return Objects.equals(mId, that.mId) + && Objects.equals(mChannelId, that.mChannelId) + && Objects.equals(mSeriesId, that.mSeriesId) + && Objects.equals(mSeasonNumber, that.mSeasonNumber) + && Objects.equals(mSeasonTitle, that.mSeasonTitle) + && Objects.equals(mEpisodeNumber, that.mEpisodeNumber) + && Objects.equals(mStartTimeUtcMillis, that.mStartTimeUtcMillis) + && Objects.equals(mEndTimeUtcMillis, that.mEndTimeUtcMillis) + && Objects.equals(mVideoWidth, that.mVideoWidth) + && Objects.equals(mVideoHeight, that.mVideoHeight) + && Objects.equals(mSearchable, that.mSearchable) + && Objects.equals(mDataBytes, that.mDataBytes) + && Objects.equals(mDurationMillis, that.mDurationMillis) + && Objects.equals(mExpireTimeUtcMillis, that.mExpireTimeUtcMillis) + && Objects.equals(mInternalProviderFlag1, that.mInternalProviderFlag1) + && Objects.equals(mInternalProviderFlag2, that.mInternalProviderFlag2) + && Objects.equals(mInternalProviderFlag3, that.mInternalProviderFlag3) + && Objects.equals(mInternalProviderFlag4, that.mInternalProviderFlag4) + && Objects.equals(mVersionNumber, that.mVersionNumber) + && Objects.equals(mTitle, that.mTitle) + && Objects.equals(mEpisodeTitle, that.mEpisodeTitle) + && Arrays.equals(mBroadcastGenres, that.mBroadcastGenres) + && Arrays.equals(mCanonicalGenres, that.mCanonicalGenres) + && Objects.equals(mShortDescription, that.mShortDescription) + && Objects.equals(mLongDescription, that.mLongDescription) + && Objects.equals(mAudioLanguage, that.mAudioLanguage) + && Arrays.equals(mContentRatings, that.mContentRatings) + && Objects.equals(mPosterArtUri, that.mPosterArtUri) + && Objects.equals(mThumbnailUri, that.mThumbnailUri); + } + + /** Hashes based on the ID. */ @Override public int hashCode() { return Objects.hash(mId); @@ -788,42 +843,80 @@ public class RecordedProgram extends BaseProgram { @Override public String toString() { return "RecordedProgram" - + "[" + mId + - "]{ mPackageName=" + mPackageName + - ", mInputId='" + mInputId + '\'' + - ", mChannelId='" + mChannelId + '\'' + - ", mTitle='" + mTitle + '\'' + - ", mSeriesId='" + mSeriesId + '\'' + - ", mEpisodeNumber=" + mEpisodeNumber + - ", mEpisodeTitle='" + mEpisodeTitle + '\'' + - ", mStartTimeUtcMillis=" + mStartTimeUtcMillis + - ", mEndTimeUtcMillis=" + mEndTimeUtcMillis + - ", mBroadcastGenres=" + - (mBroadcastGenres != null ? Arrays.toString(mBroadcastGenres) : "null") + - ", mCanonicalGenres=" + - (mCanonicalGenres != null ? Arrays.toString(mCanonicalGenres) : "null") + - ", mShortDescription='" + mShortDescription + '\'' + - ", mLongDescription='" + mLongDescription + '\'' + - ", mVideoHeight=" + mVideoHeight + - ", mVideoWidth=" + mVideoWidth + - ", mAudioLanguage='" + mAudioLanguage + '\'' + - ", mContentRatings='" + - TvContentRatingCache.contentRatingsToString(mContentRatings) + '\'' + - ", mPosterArtUri=" + mPosterArtUri + - ", mThumbnailUri=" + mThumbnailUri + - ", mSearchable=" + mSearchable + - ", mDataUri=" + mDataUri + - ", mDataBytes=" + mDataBytes + - ", mDurationMillis=" + mDurationMillis + - ", mExpireTimeUtcMillis=" + mExpireTimeUtcMillis + - ", mInternalProviderFlag1=" + mInternalProviderFlag1 + - ", mInternalProviderFlag2=" + mInternalProviderFlag2 + - ", mInternalProviderFlag3=" + mInternalProviderFlag3 + - ", mInternalProviderFlag4=" + mInternalProviderFlag4 + - ", mSeasonNumber=" + mSeasonNumber + - ", mSeasonTitle=" + mSeasonTitle + - ", mVersionNumber=" + mVersionNumber + - '}'; + + "[" + + mId + + "]{ mPackageName=" + + mPackageName + + ", mInputId='" + + mInputId + + '\'' + + ", mChannelId='" + + mChannelId + + '\'' + + ", mTitle='" + + mTitle + + '\'' + + ", mSeriesId='" + + mSeriesId + + '\'' + + ", mEpisodeNumber=" + + mEpisodeNumber + + ", mEpisodeTitle='" + + mEpisodeTitle + + '\'' + + ", mStartTimeUtcMillis=" + + mStartTimeUtcMillis + + ", mEndTimeUtcMillis=" + + mEndTimeUtcMillis + + ", mBroadcastGenres=" + + (mBroadcastGenres != null ? Arrays.toString(mBroadcastGenres) : "null") + + ", mCanonicalGenres=" + + (mCanonicalGenres != null ? Arrays.toString(mCanonicalGenres) : "null") + + ", mShortDescription='" + + mShortDescription + + '\'' + + ", mLongDescription='" + + mLongDescription + + '\'' + + ", mVideoHeight=" + + mVideoHeight + + ", mVideoWidth=" + + mVideoWidth + + ", mAudioLanguage='" + + mAudioLanguage + + '\'' + + ", mContentRatings='" + + TvContentRatingCache.contentRatingsToString(mContentRatings) + + '\'' + + ", mPosterArtUri=" + + mPosterArtUri + + ", mThumbnailUri=" + + mThumbnailUri + + ", mSearchable=" + + mSearchable + + ", mDataUri=" + + mDataUri + + ", mDataBytes=" + + mDataBytes + + ", mDurationMillis=" + + mDurationMillis + + ", mExpireTimeUtcMillis=" + + mExpireTimeUtcMillis + + ", mInternalProviderFlag1=" + + mInternalProviderFlag1 + + ", mInternalProviderFlag2=" + + mInternalProviderFlag2 + + ", mInternalProviderFlag3=" + + mInternalProviderFlag3 + + ", mInternalProviderFlag4=" + + mInternalProviderFlag4 + + ", mSeasonNumber=" + + mSeasonNumber + + ", mSeasonTitle=" + + mSeasonTitle + + ", mVersionNumber=" + + mVersionNumber + + '}'; } @Nullable @@ -836,9 +929,7 @@ public class RecordedProgram extends BaseProgram { return genres == null ? null : TvContract.Programs.Genres.encode(genres); } - /** - * Returns an array containing all of the elements in the list. - */ + /** Returns an array containing all of the elements in the list. */ public static RecordedProgram[] toArray(Collection<RecordedProgram> recordedPrograms) { return recordedPrograms.toArray(new RecordedProgram[recordedPrograms.size()]); } diff --git a/src/com/android/tv/dvr/data/ScheduledRecording.java b/src/com/android/tv/dvr/data/ScheduledRecording.java index 5d11c0f3..7c2d12d9 100644 --- a/src/com/android/tv/dvr/data/ScheduledRecording.java +++ b/src/com/android/tv/dvr/data/ScheduledRecording.java @@ -16,107 +16,97 @@ package com.android.tv.dvr.data; +import android.annotation.TargetApi; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.IntDef; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Range; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.common.util.CommonUtils; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.provider.DvrContract.Schedules; import com.android.tv.util.CompositeComparator; -import com.android.tv.util.Utils; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.Comparator; import java.util.Objects; -/** - * A data class for one recording contents. - */ +/** A data class for one recording contents. */ +@TargetApi(Build.VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated public final class ScheduledRecording implements Parcelable { private static final String TAG = "ScheduledRecording"; - /** - * Indicates that the ID is not assigned yet. - */ + /** Indicates that the ID is not assigned yet. */ public static final long ID_NOT_SET = 0; - /** - * The default priority of the recording. - */ + /** The default priority of the recording. */ public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1; - /** - * Compares the start time in ascending order. - */ - public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR - = new Comparator<ScheduledRecording>() { - @Override - public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { - return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs); - } - }; - - /** - * Compares the end time in ascending order. - */ - public static final Comparator<ScheduledRecording> END_TIME_COMPARATOR - = new Comparator<ScheduledRecording>() { - @Override - public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { - return Long.compare(lhs.mEndTimeMs, rhs.mEndTimeMs); - } - }; - - /** - * Compares ID in ascending order. The schedule with the larger ID was created later. - */ - public static final Comparator<ScheduledRecording> ID_COMPARATOR - = new Comparator<ScheduledRecording>() { - @Override - public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { - return Long.compare(lhs.mId, rhs.mId); - } - }; - - /** - * Compares the priority in ascending order. - */ - public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR - = new Comparator<ScheduledRecording>() { - @Override - public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { - return Long.compare(lhs.mPriority, rhs.mPriority); - } - }; + /** Compares the start time in ascending order. */ + public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR = + new Comparator<ScheduledRecording>() { + @Override + public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { + return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs); + } + }; + + /** Compares the end time in ascending order. */ + public static final Comparator<ScheduledRecording> END_TIME_COMPARATOR = + new Comparator<ScheduledRecording>() { + @Override + public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { + return Long.compare(lhs.mEndTimeMs, rhs.mEndTimeMs); + } + }; + + /** Compares ID in ascending order. The schedule with the larger ID was created later. */ + public static final Comparator<ScheduledRecording> ID_COMPARATOR = + new Comparator<ScheduledRecording>() { + @Override + public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { + return Long.compare(lhs.mId, rhs.mId); + } + }; + + /** Compares the priority in ascending order. */ + public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR = + new Comparator<ScheduledRecording>() { + @Override + public int compare(ScheduledRecording lhs, ScheduledRecording rhs) { + return Long.compare(lhs.mPriority, rhs.mPriority); + } + }; /** * Compares start time in ascending order and then priority in descending order and then ID in * descending order. */ - public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR - = new CompositeComparator<>(START_TIME_COMPARATOR, PRIORITY_COMPARATOR.reversed(), - ID_COMPARATOR.reversed()); + public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR = + new CompositeComparator<>( + START_TIME_COMPARATOR, + PRIORITY_COMPARATOR.reversed(), + ID_COMPARATOR.reversed()); - /** - * Builds scheduled recordings from programs. - */ + /** Builds scheduled recordings from programs. */ public static Builder builder(String inputId, Program p) { return new Builder() .setInputId(inputId) .setChannelId(p.getChannelId()) - .setStartTimeMs(p.getStartTimeUtcMillis()).setEndTimeMs(p.getEndTimeUtcMillis()) + .setStartTimeMs(p.getStartTimeUtcMillis()) + .setEndTimeMs(p.getEndTimeUtcMillis()) .setProgramId(p.getId()) .setProgramTitle(p.getTitle()) .setSeasonNumber(p.getSeasonNumber()) @@ -138,9 +128,7 @@ public final class ScheduledRecording implements Parcelable { .setType(TYPE_TIMED); } - /** - * Creates a new Builder with the values set from the {@link RecordedProgram}. - */ + /** Creates a new Builder with the values set from the {@link RecordedProgram}. */ public static Builder builder(RecordedProgram p) { boolean isProgramRecording = !TextUtils.isEmpty(p.getTitle()); return new Builder() @@ -157,7 +145,8 @@ public final class ScheduledRecording implements Parcelable { .setProgramLongDescription(p.getLongDescription()) .setProgramPosterArtUri(p.getPosterArtUri()) .setProgramThumbnailUri(p.getThumbnailUri()) - .setState(STATE_RECORDING_FINISHED); + .setState(STATE_RECORDING_FINISHED) + .setRecordedProgramId(p.getId()); } public static final class Builder { @@ -179,8 +168,10 @@ public final class ScheduledRecording implements Parcelable { private String mProgramThumbnailUri; private @RecordingState int mState; private long mSeriesRecordingId = ID_NOT_SET; + private Long mRecodedProgramId; + private Integer mFailedReason; - private Builder() { } + private Builder() {} public Builder setId(long id) { mId = id; @@ -272,17 +263,42 @@ public final class ScheduledRecording implements Parcelable { return this; } + public Builder setRecordedProgramId(Long recordedProgramId) { + mRecodedProgramId = recordedProgramId; + return this; + } + + public Builder setFailedReason(Integer reason) { + mFailedReason = reason; + return this; + } + public ScheduledRecording build() { - return new ScheduledRecording(mId, mPriority, mInputId, mChannelId, mProgramId, - mProgramTitle, mType, mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber, - mEpisodeTitle, mProgramDescription, mProgramLongDescription, - mProgramPosterArtUri, mProgramThumbnailUri, mState, mSeriesRecordingId); + return new ScheduledRecording( + mId, + mPriority, + mInputId, + mChannelId, + mProgramId, + mProgramTitle, + mType, + mStartTimeMs, + mEndTimeMs, + mSeasonNumber, + mEpisodeNumber, + mEpisodeTitle, + mProgramDescription, + mProgramLongDescription, + mProgramPosterArtUri, + mProgramThumbnailUri, + mState, + mSeriesRecordingId, + mRecodedProgramId, + mFailedReason); } } - /** - * Creates {@link Builder} object from the given original {@code Recording}. - */ + /** Creates {@link Builder} object from the given original {@code Recording}. */ public static Builder buildFrom(ScheduledRecording orig) { return new Builder() .setId(orig.mId) @@ -301,14 +317,23 @@ public final class ScheduledRecording implements Parcelable { .setProgramLongDescription(orig.getProgramLongDescription()) .setProgramPosterArtUri(orig.getProgramPosterArtUri()) .setProgramThumbnailUri(orig.getProgramThumbnailUri()) - .setState(orig.mState).setType(orig.mType); + .setState(orig.mState) + .setFailedReason(orig.getFailedReason()) + .setType(orig.mType); } @Retention(RetentionPolicy.SOURCE) - @IntDef({STATE_RECORDING_NOT_STARTED, STATE_RECORDING_IN_PROGRESS, STATE_RECORDING_FINISHED, - STATE_RECORDING_FAILED, STATE_RECORDING_CLIPPED, STATE_RECORDING_DELETED, - STATE_RECORDING_CANCELED}) + @IntDef({ + STATE_RECORDING_NOT_STARTED, + STATE_RECORDING_IN_PROGRESS, + STATE_RECORDING_FINISHED, + STATE_RECORDING_FAILED, + STATE_RECORDING_CLIPPED, + STATE_RECORDING_DELETED, + STATE_RECORDING_CANCELED + }) public @interface RecordingState {} + public static final int STATE_RECORDING_NOT_STARTED = 0; public static final int STATE_RECORDING_IN_PROGRESS = 1; public static final int STATE_RECORDING_FINISHED = 2; @@ -317,48 +342,74 @@ public final class ScheduledRecording implements Parcelable { public static final int STATE_RECORDING_DELETED = 5; public static final int STATE_RECORDING_CANCELED = 6; + /** The reasons of failed recordings */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + FAILED_REASON_OTHER, + FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED, + FAILED_REASON_NOT_FINISHED, + FAILED_REASON_SCHEDULER_STOPPED, + FAILED_REASON_INVALID_CHANNEL, + FAILED_REASON_MESSAGE_NOT_SENT, + FAILED_REASON_CONNECTION_FAILED, + FAILED_REASON_RESOURCE_BUSY, + FAILED_REASON_INPUT_UNAVAILABLE, + FAILED_REASON_INPUT_DVR_UNSUPPORTED, + FAILED_REASON_INSUFFICIENT_SPACE + }) + public @interface RecordingFailedReason {} + + public static final int FAILED_REASON_OTHER = 0; + public static final int FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED = 1; + public static final int FAILED_REASON_NOT_FINISHED = 2; + public static final int FAILED_REASON_SCHEDULER_STOPPED = 3; + public static final int FAILED_REASON_INVALID_CHANNEL = 4; + public static final int FAILED_REASON_MESSAGE_NOT_SENT = 5; + public static final int FAILED_REASON_CONNECTION_FAILED = 6; + public static final int FAILED_REASON_RESOURCE_BUSY = 7; + // For the following reasons, show advice to users + public static final int FAILED_REASON_INPUT_UNAVAILABLE = 8; + public static final int FAILED_REASON_INPUT_DVR_UNSUPPORTED = 9; + public static final int FAILED_REASON_INSUFFICIENT_SPACE = 10; + @Retention(RetentionPolicy.SOURCE) @IntDef({TYPE_TIMED, TYPE_PROGRAM}) public @interface RecordingType {} - /** - * Record with given time range. - */ + /** Record with given time range. */ public static final int TYPE_TIMED = 1; - /** - * Record with a given program. - */ + /** Record with a given program. */ public static final int TYPE_PROGRAM = 2; @RecordingType private final int mType; /** - * Use this projection if you want to create {@link ScheduledRecording} object using - * {@link #fromCursor}. + * Use this projection if you want to create {@link ScheduledRecording} object using {@link + * #fromCursor}. */ public static final String[] PROJECTION = { - // Columns must match what is read in #fromCursor - Schedules._ID, - Schedules.COLUMN_PRIORITY, - Schedules.COLUMN_TYPE, - Schedules.COLUMN_INPUT_ID, - Schedules.COLUMN_CHANNEL_ID, - Schedules.COLUMN_PROGRAM_ID, - Schedules.COLUMN_PROGRAM_TITLE, - Schedules.COLUMN_START_TIME_UTC_MILLIS, - Schedules.COLUMN_END_TIME_UTC_MILLIS, - Schedules.COLUMN_SEASON_NUMBER, - Schedules.COLUMN_EPISODE_NUMBER, - Schedules.COLUMN_EPISODE_TITLE, - Schedules.COLUMN_PROGRAM_DESCRIPTION, - Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, - Schedules.COLUMN_PROGRAM_POST_ART_URI, - Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, - Schedules.COLUMN_STATE, - Schedules.COLUMN_SERIES_RECORDING_ID}; + // Columns must match what is read in #fromCursor + Schedules._ID, + Schedules.COLUMN_PRIORITY, + Schedules.COLUMN_TYPE, + Schedules.COLUMN_INPUT_ID, + Schedules.COLUMN_CHANNEL_ID, + Schedules.COLUMN_PROGRAM_ID, + Schedules.COLUMN_PROGRAM_TITLE, + Schedules.COLUMN_START_TIME_UTC_MILLIS, + Schedules.COLUMN_END_TIME_UTC_MILLIS, + Schedules.COLUMN_SEASON_NUMBER, + Schedules.COLUMN_EPISODE_NUMBER, + Schedules.COLUMN_EPISODE_TITLE, + Schedules.COLUMN_PROGRAM_DESCRIPTION, + Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, + Schedules.COLUMN_PROGRAM_POST_ART_URI, + Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, + Schedules.COLUMN_STATE, + Schedules.COLUMN_FAILED_REASON, + Schedules.COLUMN_SERIES_RECORDING_ID + }; - /** - * Creates {@link ScheduledRecording} object from the given {@link Cursor}. - */ + /** Creates {@link ScheduledRecording} object from the given {@link Cursor}. */ public static ScheduledRecording fromCursor(Cursor c) { int index = -1; return new Builder() @@ -379,6 +430,7 @@ public final class ScheduledRecording implements Parcelable { .setProgramPosterArtUri(c.getString(++index)) .setProgramThumbnailUri(c.getString(++index)) .setState(recordingState(c.getString(++index))) + .setFailedReason(recordingFailedReason(c.getString(++index))) .setSeriesRecordingId(c.getLong(++index)) .build(); } @@ -403,6 +455,7 @@ public final class ScheduledRecording implements Parcelable { values.put(Schedules.COLUMN_PROGRAM_POST_ART_URI, r.getProgramPosterArtUri()); values.put(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, r.getProgramThumbnailUri()); values.put(Schedules.COLUMN_STATE, recordingState(r.getState())); + values.put(Schedules.COLUMN_FAILED_REASON, recordingFailedReason(r.getFailedReason())); values.put(Schedules.COLUMN_TYPE, recordingType(r.getType())); if (r.getSeriesRecordingId() != ID_NOT_SET) { values.put(Schedules.COLUMN_SERIES_RECORDING_ID, r.getSeriesRecordingId()); @@ -431,42 +484,40 @@ public final class ScheduledRecording implements Parcelable { .setProgramPosterArtUri(in.readString()) .setProgramThumbnailUri(in.readString()) .setState(in.readInt()) + .setFailedReason(recordingFailedReason(in.readString())) .setSeriesRecordingId(in.readLong()) .build(); } public static final Parcelable.Creator<ScheduledRecording> CREATOR = new Parcelable.Creator<ScheduledRecording>() { - @Override - public ScheduledRecording createFromParcel(Parcel in) { - return ScheduledRecording.fromParcel(in); - } - - @Override - public ScheduledRecording[] newArray(int size) { - return new ScheduledRecording[size]; - } - }; - - /** - * The ID internal to Live TV - */ + @Override + public ScheduledRecording createFromParcel(Parcel in) { + return ScheduledRecording.fromParcel(in); + } + + @Override + public ScheduledRecording[] newArray(int size) { + return new ScheduledRecording[size]; + } + }; + + /** The ID internal to Live TV */ private long mId; /** * The priority of this recording. * - * <p> The highest number is recorded first. If there is a tie in priority then the higher id + * <p>The highest number is recorded first. If there is a tie in priority then the higher id * wins. */ private final long mPriority; private final String mInputId; private final long mChannelId; - /** - * Optional id of the associated program. - */ + /** Optional id of the associated program. */ private final long mProgramId; + private final String mProgramTitle; private final long mStartTimeMs; @@ -480,12 +531,30 @@ public final class ScheduledRecording implements Parcelable { private final String mProgramThumbnailUri; @RecordingState private final int mState; private final long mSeriesRecordingId; - - private ScheduledRecording(long id, long priority, String inputId, long channelId, long programId, - String programTitle, @RecordingType int type, long startTime, long endTime, - String seasonNumber, String episodeNumber, String episodeTitle, - String programDescription, String programLongDescription, String programPosterArtUri, - String programThumbnailUri, @RecordingState int state, long seriesRecordingId) { + private final Long mRecordedProgramId; + private final Integer mFailedReason; + + private ScheduledRecording( + long id, + long priority, + String inputId, + long channelId, + long programId, + String programTitle, + @RecordingType int type, + long startTime, + long endTime, + String seasonNumber, + String episodeNumber, + String episodeTitle, + String programDescription, + String programLongDescription, + String programPosterArtUri, + String programThumbnailUri, + @RecordingState int state, + long seriesRecordingId, + Long recordedProgramId, + Integer failedReason) { mId = id; mPriority = priority; mInputId = inputId; @@ -504,139 +573,122 @@ public final class ScheduledRecording implements Parcelable { mProgramThumbnailUri = programThumbnailUri; mState = state; mSeriesRecordingId = seriesRecordingId; + mRecordedProgramId = recordedProgramId; + mFailedReason = failedReason; } /** - * Returns recording schedule type. The possible types are {@link #TYPE_PROGRAM} and - * {@link #TYPE_TIMED}. + * Returns recording schedule type. The possible types are {@link #TYPE_PROGRAM} and {@link + * #TYPE_TIMED}. */ @RecordingType public int getType() { return mType; } - /** - * Returns schedules' input id. - */ + /** Returns schedules' input id. */ public String getInputId() { return mInputId; } - /** - * Returns recorded {@link Channel}. - */ + /** Returns recorded {@link Channel}. */ public long getChannelId() { return mChannelId; } - /** - * Return the optional program id - */ + /** Return the optional program id */ public long getProgramId() { return mProgramId; } - /** - * Return the optional program Title - */ + /** Return the optional program Title */ public String getProgramTitle() { return mProgramTitle; } - /** - * Returns started time. - */ + /** Returns started time. */ public long getStartTimeMs() { return mStartTimeMs; } - /** - * Returns ended time. - */ + /** Returns ended time. */ public long getEndTimeMs() { return mEndTimeMs; } - /** - * Returns the season number. - */ + /** Returns the season number. */ public String getSeasonNumber() { return mSeasonNumber; } - /** - * Returns the episode number. - */ + /** Returns the episode number. */ public String getEpisodeNumber() { return mEpisodeNumber; } - /** - * Returns the episode title. - */ + /** Returns the episode title. */ public String getEpisodeTitle() { return mEpisodeTitle; } - /** - * Returns the description of program. - */ + /** Returns the description of program. */ public String getProgramDescription() { return mProgramDescription; } - /** - * Returns the long description of program. - */ + /** Returns the long description of program. */ public String getProgramLongDescription() { return mProgramLongDescription; } - /** - * Returns the poster uri of program. - */ + /** Returns the poster uri of program. */ public String getProgramPosterArtUri() { return mProgramPosterArtUri; } - /** - * Returns the thumb nail uri of program. - */ + /** Returns the thumb nail uri of program. */ public String getProgramThumbnailUri() { return mProgramThumbnailUri; } - /** - * Returns duration. - */ + /** Returns duration. */ public long getDuration() { return mEndTimeMs - mStartTimeMs; } /** - * Returns the state. The possible states are {@link #STATE_RECORDING_NOT_STARTED}, - * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, - * {@link #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and - * {@link #STATE_RECORDING_DELETED}. + * Returns the state. The possible states are {@link #STATE_RECORDING_NOT_STARTED}, {@link + * #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, {@link + * #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and {@link + * #STATE_RECORDING_DELETED}. */ - @RecordingState public int getState() { + @RecordingState + public int getState() { return mState; } - /** - * Returns the ID of the {@link SeriesRecording} including this schedule. - */ + /** Returns the ID of the {@link SeriesRecording} including this schedule. */ public long getSeriesRecordingId() { return mSeriesRecordingId; } + /** Returns the ID of the corresponding {@link RecordedProgram}. */ + @Nullable + public Long getRecordedProgramId() { + return mRecordedProgramId; + } + + /** Returns the failed reason of the {@link ScheduledRecording}. */ + @Nullable @RecordingFailedReason + public Integer getFailedReason() { + return mFailedReason; + } + public long getId() { return mId; } - /** - * Sets the ID; - */ + /** Sets the ID; */ public void setId(long id) { mId = id; } @@ -645,21 +697,23 @@ public final class ScheduledRecording implements Parcelable { return mPriority; } - /** - * Returns season number, episode number and episode title for display. - */ + /** Returns season number, episode number and episode title for display. */ public String getEpisodeDisplayTitle(Context context) { if (!TextUtils.isEmpty(mEpisodeNumber)) { String episodeTitle = mEpisodeTitle == null ? "" : mEpisodeTitle; if (TextUtils.equals(mSeasonNumber, "0")) { // Do not show "S0: ". - return String.format(context.getResources().getString( - R.string.display_episode_title_format_no_season_number), - mEpisodeNumber, episodeTitle); + return String.format( + context.getResources() + .getString(R.string.display_episode_title_format_no_season_number), + mEpisodeNumber, + episodeTitle); } else { - return String.format(context.getResources().getString( - R.string.display_episode_title_format), - mSeasonNumber, mEpisodeNumber, episodeTitle); + return String.format( + context.getResources().getString(R.string.display_episode_title_format), + mSeasonNumber, + mEpisodeNumber, + episodeTitle); } } return mEpisodeTitle; @@ -673,15 +727,14 @@ public final class ScheduledRecording implements Parcelable { if (!TextUtils.isEmpty(mProgramTitle)) { return mProgramTitle; } - Channel channel = TvApplication.getSingletons(context).getChannelDataManager() - .getChannel(mChannelId); - return channel != null ? channel.getDisplayName() + Channel channel = + TvSingletons.getSingletons(context).getChannelDataManager().getChannel(mChannelId); + return channel != null + ? channel.getDisplayName() : context.getString(R.string.no_program_information); } - /** - * Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}. - */ + /** Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}. */ private static @RecordingType int recordingType(String type) { switch (type) { case Schedules.TYPE_TIMED: @@ -689,14 +742,12 @@ public final class ScheduledRecording implements Parcelable { case Schedules.TYPE_PROGRAM: return TYPE_PROGRAM; default: - SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type); + SoftPreconditions.checkArgument(false, TAG, "Unknown recording type %s", type); return TYPE_TIMED; } } - /** - * Converts a @RecordingType int to a string, defaulting to {@link Schedules#TYPE_TIMED}. - */ + /** Converts a @RecordingType int to a string, defaulting to {@link Schedules#TYPE_TIMED}. */ private static String recordingType(@RecordingType int type) { switch (type) { case TYPE_TIMED: @@ -704,14 +755,14 @@ public final class ScheduledRecording implements Parcelable { case TYPE_PROGRAM: return Schedules.TYPE_PROGRAM; default: - SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type); + SoftPreconditions.checkArgument(false, TAG, "Unknown recording type %s", type); return Schedules.TYPE_TIMED; } } /** - * Converts a string to a @RecordingState int, defaulting to - * {@link #STATE_RECORDING_NOT_STARTED}. + * Converts a string to a @RecordingState int, defaulting to {@link + * #STATE_RECORDING_NOT_STARTED}. */ private static @RecordingState int recordingState(String state) { switch (state) { @@ -730,14 +781,14 @@ public final class ScheduledRecording implements Parcelable { case Schedules.STATE_RECORDING_CANCELED: return STATE_RECORDING_CANCELED; default: - SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state); + SoftPreconditions.checkArgument(false, TAG, "Unknown recording state %s", state); return STATE_RECORDING_NOT_STARTED; } } /** - * Converts a @RecordingState int to string, defaulting to - * {@link Schedules#STATE_RECORDING_NOT_STARTED}. + * Converts a @RecordingState int to string, defaulting to {@link + * Schedules#STATE_RECORDING_NOT_STARTED}. */ private static String recordingState(@RecordingState int state) { switch (state) { @@ -756,46 +807,138 @@ public final class ScheduledRecording implements Parcelable { case STATE_RECORDING_CANCELED: return Schedules.STATE_RECORDING_CANCELED; default: - SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state); + SoftPreconditions.checkArgument(false, TAG, "Unknown recording state %s", state); return Schedules.STATE_RECORDING_NOT_STARTED; } } /** - * Checks if the {@code period} overlaps with the recording time. + * Converts a string to a failed reason integer, defaulting to {@link + * #FAILED_REASON_OTHER}. */ - public boolean isOverLapping(Range<Long> period) { - return mStartTimeMs < period.getUpper() && mEndTimeMs > period.getLower(); + private static Integer recordingFailedReason(String reason) { + if (TextUtils.isEmpty(reason)) { + return null; + } + switch (reason) { + case Schedules.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED: + return FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED; + case Schedules.FAILED_REASON_NOT_FINISHED: + return FAILED_REASON_NOT_FINISHED; + case Schedules.FAILED_REASON_SCHEDULER_STOPPED: + return FAILED_REASON_SCHEDULER_STOPPED; + case Schedules.FAILED_REASON_INVALID_CHANNEL: + return FAILED_REASON_INVALID_CHANNEL; + case Schedules.FAILED_REASON_MESSAGE_NOT_SENT: + return FAILED_REASON_MESSAGE_NOT_SENT; + case Schedules.FAILED_REASON_CONNECTION_FAILED: + return FAILED_REASON_CONNECTION_FAILED; + case Schedules.FAILED_REASON_RESOURCE_BUSY: + return FAILED_REASON_RESOURCE_BUSY; + case Schedules.FAILED_REASON_INPUT_UNAVAILABLE: + return FAILED_REASON_INPUT_UNAVAILABLE; + case Schedules.FAILED_REASON_INPUT_DVR_UNSUPPORTED: + return FAILED_REASON_INPUT_DVR_UNSUPPORTED; + case Schedules.FAILED_REASON_INSUFFICIENT_SPACE: + return FAILED_REASON_INSUFFICIENT_SPACE; + case Schedules.FAILED_REASON_OTHER: + default: + return FAILED_REASON_OTHER; + } } /** - * Checks if the {@code schedule} overlaps with this schedule. + * Converts a failed reason integer to string, defaulting to {@link + * Schedules#FAILED_REASON_OTHER}. */ + private static String recordingFailedReason(Integer reason) { + if (reason == null) { + return null; + } + switch (reason) { + case FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED: + return Schedules.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED; + case FAILED_REASON_NOT_FINISHED: + return Schedules.FAILED_REASON_NOT_FINISHED; + case FAILED_REASON_SCHEDULER_STOPPED: + return Schedules.FAILED_REASON_SCHEDULER_STOPPED; + case FAILED_REASON_INVALID_CHANNEL: + return Schedules.FAILED_REASON_INVALID_CHANNEL; + case FAILED_REASON_MESSAGE_NOT_SENT: + return Schedules.FAILED_REASON_MESSAGE_NOT_SENT; + case FAILED_REASON_CONNECTION_FAILED: + return Schedules.FAILED_REASON_CONNECTION_FAILED; + case FAILED_REASON_RESOURCE_BUSY: + return Schedules.FAILED_REASON_RESOURCE_BUSY; + case FAILED_REASON_INPUT_UNAVAILABLE: + return Schedules.FAILED_REASON_INPUT_UNAVAILABLE; + case FAILED_REASON_INPUT_DVR_UNSUPPORTED: + return Schedules.FAILED_REASON_INPUT_DVR_UNSUPPORTED; + case FAILED_REASON_INSUFFICIENT_SPACE: + return Schedules.FAILED_REASON_INSUFFICIENT_SPACE; + case FAILED_REASON_OTHER: // fall through + default: + return Schedules.FAILED_REASON_OTHER; + } + } + + /** Checks if the {@code period} overlaps with the recording time. */ + public boolean isOverLapping(Range<Long> period) { + return mStartTimeMs < period.getUpper() && mEndTimeMs > period.getLower(); + } + + /** Checks if the {@code schedule} overlaps with this schedule. */ public boolean isOverLapping(ScheduledRecording schedule) { return mStartTimeMs < schedule.getEndTimeMs() && mEndTimeMs > schedule.getStartTimeMs(); } @Override public String toString() { - return "ScheduledRecording[" + mId + return "ScheduledRecording[" + + mId + "]" - + "(inputId=" + mInputId - + ",channelId=" + mChannelId - + ",programId=" + mProgramId - + ",programTitle=" + mProgramTitle - + ",type=" + mType - + ",startTime=" + Utils.toIsoDateTimeString(mStartTimeMs) + "(" + mStartTimeMs + ")" - + ",endTime=" + Utils.toIsoDateTimeString(mEndTimeMs) + "(" + mEndTimeMs + ")" - + ",seasonNumber=" + mSeasonNumber - + ",episodeNumber=" + mEpisodeNumber - + ",episodeTitle=" + mEpisodeTitle - + ",programDescription=" + mProgramDescription - + ",programLongDescription=" + mProgramLongDescription - + ",programPosterArtUri=" + mProgramPosterArtUri - + ",programThumbnailUri=" + mProgramThumbnailUri - + ",state=" + mState - + ",priority=" + mPriority - + ",seriesRecordingId=" + mSeriesRecordingId + + "(inputId=" + + mInputId + + ",channelId=" + + mChannelId + + ",programId=" + + mProgramId + + ",programTitle=" + + mProgramTitle + + ",type=" + + mType + + ",startTime=" + + CommonUtils.toIsoDateTimeString(mStartTimeMs) + + "(" + + mStartTimeMs + + ")" + + ",endTime=" + + CommonUtils.toIsoDateTimeString(mEndTimeMs) + + "(" + + mEndTimeMs + + ")" + + ",seasonNumber=" + + mSeasonNumber + + ",episodeNumber=" + + mEpisodeNumber + + ",episodeTitle=" + + mEpisodeTitle + + ",programDescription=" + + mProgramDescription + + ",programLongDescription=" + + mProgramLongDescription + + ",programPosterArtUri=" + + mProgramPosterArtUri + + ",programThumbnailUri=" + + mProgramThumbnailUri + + ",state=" + + mState + + ",failedReason=" + + mFailedReason + + ",priority=" + + mPriority + + ",seriesRecordingId=" + + mSeriesRecordingId + ")"; } @@ -823,23 +966,25 @@ public final class ScheduledRecording implements Parcelable { out.writeString(mProgramPosterArtUri); out.writeString(mProgramThumbnailUri); out.writeInt(mState); + out.writeString(recordingFailedReason(mFailedReason)); out.writeLong(mSeriesRecordingId); } - /** - * Returns {@code true} if the recording is not started yet, otherwise @{code false}. - */ + /** Returns {@code true} if the recording is not started yet, otherwise @{code false}. */ public boolean isNotStarted() { return mState == STATE_RECORDING_NOT_STARTED; } - /** - * Returns {@code true} if the recording is in progress, otherwise @{code false}. - */ + /** Returns {@code true} if the recording is in progress, otherwise @{code false}. */ public boolean isInProgress() { return mState == STATE_RECORDING_IN_PROGRESS; } + /** Returns {@code true} if the recording is finished, otherwise @{code false}. */ + public boolean isFinished() { + return mState == STATE_RECORDING_FINISHED; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof ScheduledRecording)) { @@ -862,20 +1007,34 @@ public final class ScheduledRecording implements Parcelable { && Objects.equals(mProgramPosterArtUri, r.getProgramPosterArtUri()) && Objects.equals(mProgramThumbnailUri, r.getProgramThumbnailUri()) && mState == r.mState + && Objects.equals(mFailedReason, r.mFailedReason) && mSeriesRecordingId == r.mSeriesRecordingId; } @Override public int hashCode() { - return Objects.hash(mId, mPriority, mChannelId, mProgramId, mProgramTitle, mType, - mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber, mEpisodeTitle, - mProgramDescription, mProgramLongDescription, mProgramPosterArtUri, - mProgramThumbnailUri, mState, mSeriesRecordingId); + return Objects.hash( + mId, + mPriority, + mChannelId, + mProgramId, + mProgramTitle, + mType, + mStartTimeMs, + mEndTimeMs, + mSeasonNumber, + mEpisodeNumber, + mEpisodeTitle, + mProgramDescription, + mProgramLongDescription, + mProgramPosterArtUri, + mProgramThumbnailUri, + mState, + mFailedReason, + mSeriesRecordingId); } - /** - * Returns an array containing all of the elements in the list. - */ + /** Returns an array containing all of the elements in the list. */ public static ScheduledRecording[] toArray(Collection<ScheduledRecording> schedules) { return schedules.toArray(new ScheduledRecording[schedules.size()]); } diff --git a/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java b/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java index 89533dbb..c697451a 100644 --- a/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java +++ b/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java @@ -17,20 +17,15 @@ package com.android.tv.dvr.data; import android.text.TextUtils; - import java.util.Objects; -/** - * A plain java object which includes the season/episode number for the series recording. - */ +/** A plain java object which includes the season/episode number for the series recording. */ public class SeasonEpisodeNumber { public final long seriesRecordingId; public final String seasonNumber; public final String episodeNumber; - /** - * Creates a new Builder with the values set from an existing {@link ScheduledRecording}. - */ + /** Creates a new Builder with the values set from an existing {@link ScheduledRecording}. */ public SeasonEpisodeNumber(ScheduledRecording r) { this(r.getSeriesRecordingId(), r.getSeasonNumber(), r.getEpisodeNumber()); } @@ -47,7 +42,8 @@ public class SeasonEpisodeNumber { return true; } if (!(o instanceof SeasonEpisodeNumber) - || TextUtils.isEmpty(seasonNumber) || TextUtils.isEmpty(episodeNumber)) { + || TextUtils.isEmpty(seasonNumber) + || TextUtils.isEmpty(episodeNumber)) { return false; } SeasonEpisodeNumber that = (SeasonEpisodeNumber) o; @@ -63,10 +59,13 @@ public class SeasonEpisodeNumber { @Override public String toString() { - return "SeasonEpisodeNumber{" + - "seriesRecordingId=" + seriesRecordingId + - ", seasonNumber='" + seasonNumber + - ", episodeNumber=" + episodeNumber + - '}'; + return "SeasonEpisodeNumber{" + + "seriesRecordingId=" + + seriesRecordingId + + ", seasonNumber='" + + seasonNumber + + ", episodeNumber=" + + episodeNumber + + '}'; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/data/SeriesInfo.java b/src/com/android/tv/dvr/data/SeriesInfo.java index a0dec4a4..aa622c3d 100644 --- a/src/com/android/tv/dvr/data/SeriesInfo.java +++ b/src/com/android/tv/dvr/data/SeriesInfo.java @@ -16,9 +16,7 @@ package com.android.tv.dvr.data; -/** - * Series information. - */ +/** Series information. */ public class SeriesInfo { private final String mId; private final String mTitle; @@ -28,8 +26,14 @@ public class SeriesInfo { private final String mPosterUri; private final String mPhotoUri; - public SeriesInfo(String id, String title, String description, String longDescription, - int[] canonicalGenreIds, String posterUri, String photoUri) { + public SeriesInfo( + String id, + String title, + String description, + String longDescription, + int[] canonicalGenreIds, + String posterUri, + String photoUri) { this.mId = id; this.mTitle = title; this.mDescription = description; @@ -39,37 +43,37 @@ public class SeriesInfo { this.mPhotoUri = photoUri; } - /** Returns the ID. **/ + /** Returns the ID. * */ public String getId() { return mId; } - /** Returns the title. **/ + /** Returns the title. * */ public String getTitle() { return mTitle; } - /** Returns the description. **/ + /** Returns the description. * */ public String getDescription() { return mDescription; } - /** Returns the description. **/ + /** Returns the description. * */ public String getLongDescription() { return mLongDescription; } - /** Returns the canonical genre IDs. **/ + /** Returns the canonical genre IDs. * */ public int[] getCanonicalGenreIds() { return mCanonicalGenreIds; } - /** Returns the poster URI. **/ + /** Returns the poster URI. * */ public String getPosterUri() { return mPosterUri; } - /** Returns the photo URI. **/ + /** Returns the photo URI. * */ public String getPhotoUri() { return mPhotoUri; } diff --git a/src/com/android/tv/dvr/data/SeriesRecording.java b/src/com/android/tv/dvr/data/SeriesRecording.java index 822e7320..96b3425a 100644 --- a/src/com/android/tv/dvr/data/SeriesRecording.java +++ b/src/com/android/tv/dvr/data/SeriesRecording.java @@ -21,15 +21,12 @@ import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.IntDef; -import android.support.annotation.VisibleForTesting; import android.text.TextUtils; - import com.android.tv.data.BaseProgram; import com.android.tv.data.Program; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.provider.DvrContract.SeriesRecordings; import com.android.tv.util.Utils; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; @@ -40,68 +37,55 @@ import java.util.Objects; /** * Schedules the recording of a Series of Programs. * - * <p> - * Contains the data needed to create new ScheduleRecordings as the programs become available in + * <p>Contains the data needed to create new ScheduleRecordings as the programs become available in * the EPG. */ public class SeriesRecording implements Parcelable { - /** - * Indicates that the ID is not assigned yet. - */ + /** Indicates that the ID is not assigned yet. */ public static final long ID_NOT_SET = 0; - /** - * The default priority of this recording. - */ + /** The default priority of this recording. */ public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1; @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {OPTION_CHANNEL_ONE, OPTION_CHANNEL_ALL}) + @IntDef( + flag = true, + value = {OPTION_CHANNEL_ONE, OPTION_CHANNEL_ALL} + ) public @interface ChannelOption {} - /** - * An option which indicates that the episodes in one channel are recorded. - */ + /** An option which indicates that the episodes in one channel are recorded. */ public static final int OPTION_CHANNEL_ONE = 0; - /** - * An option which indicates that the episodes in all the channels are recorded. - */ + /** An option which indicates that the episodes in all the channels are recorded. */ public static final int OPTION_CHANNEL_ALL = 1; @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {STATE_SERIES_NORMAL, STATE_SERIES_STOPPED}) + @IntDef( + flag = true, + value = {STATE_SERIES_NORMAL, STATE_SERIES_STOPPED} + ) public @interface SeriesState {} - /** - * The state indicates that the series recording is a normal one. - */ + /** The state indicates that the series recording is a normal one. */ public static final int STATE_SERIES_NORMAL = 0; - /** - * The state indicates that the series recording is stopped. - */ + /** The state indicates that the series recording is stopped. */ public static final int STATE_SERIES_STOPPED = 1; - /** - * Compare priority in descending order. - */ + /** Compare priority in descending order. */ public static final Comparator<SeriesRecording> PRIORITY_COMPARATOR = new Comparator<SeriesRecording>() { - @Override - public int compare(SeriesRecording lhs, SeriesRecording rhs) { - int value = Long.compare(rhs.mPriority, lhs.mPriority); - if (value == 0) { - // New recording has the higher priority. - value = Long.compare(rhs.mId, lhs.mId); - } - return value; - } - }; + @Override + public int compare(SeriesRecording lhs, SeriesRecording rhs) { + int value = Long.compare(rhs.mPriority, lhs.mPriority); + if (value == 0) { + // New recording has the higher priority. + value = Long.compare(rhs.mId, lhs.mId); + } + return value; + } + }; - /** - * Compare ID in ascending order. - */ + /** Compare ID in ascending order. */ public static final Comparator<SeriesRecording> ID_COMPARATOR = new Comparator<SeriesRecording>() { @Override @@ -126,9 +110,7 @@ public class SeriesRecording implements Parcelable { .setPhotoUri(p.getThumbnailUri()); } - /** - * Creates a new Builder with the values set from an existing {@link SeriesRecording}. - */ + /** Creates a new Builder with the values set from an existing {@link SeriesRecording}. */ public static Builder buildFrom(SeriesRecording r) { return new Builder() .setId(r.mId) @@ -149,30 +131,28 @@ public class SeriesRecording implements Parcelable { } /** - * Use this projection if you want to create {@link SeriesRecording} object using - * {@link #fromCursor}. + * Use this projection if you want to create {@link SeriesRecording} object using {@link + * #fromCursor}. */ public static final String[] PROJECTION = { - // Columns must match what is read in fromCursor() - SeriesRecordings._ID, - SeriesRecordings.COLUMN_INPUT_ID, - SeriesRecordings.COLUMN_CHANNEL_ID, - SeriesRecordings.COLUMN_PRIORITY, - SeriesRecordings.COLUMN_TITLE, - SeriesRecordings.COLUMN_SHORT_DESCRIPTION, - SeriesRecordings.COLUMN_LONG_DESCRIPTION, - SeriesRecordings.COLUMN_SERIES_ID, - SeriesRecordings.COLUMN_START_FROM_EPISODE, - SeriesRecordings.COLUMN_START_FROM_SEASON, - SeriesRecordings.COLUMN_CHANNEL_OPTION, - SeriesRecordings.COLUMN_CANONICAL_GENRE, - SeriesRecordings.COLUMN_POSTER_URI, - SeriesRecordings.COLUMN_PHOTO_URI, - SeriesRecordings.COLUMN_STATE + // Columns must match what is read in fromCursor() + SeriesRecordings._ID, + SeriesRecordings.COLUMN_INPUT_ID, + SeriesRecordings.COLUMN_CHANNEL_ID, + SeriesRecordings.COLUMN_PRIORITY, + SeriesRecordings.COLUMN_TITLE, + SeriesRecordings.COLUMN_SHORT_DESCRIPTION, + SeriesRecordings.COLUMN_LONG_DESCRIPTION, + SeriesRecordings.COLUMN_SERIES_ID, + SeriesRecordings.COLUMN_START_FROM_EPISODE, + SeriesRecordings.COLUMN_START_FROM_SEASON, + SeriesRecordings.COLUMN_CHANNEL_OPTION, + SeriesRecordings.COLUMN_CANONICAL_GENRE, + SeriesRecordings.COLUMN_POSTER_URI, + SeriesRecordings.COLUMN_PHOTO_URI, + SeriesRecordings.COLUMN_STATE }; - /** - * Creates {@link SeriesRecording} object from the given {@link Cursor}. - */ + /** Creates {@link SeriesRecording} object from the given {@link Cursor}. */ public static SeriesRecording fromCursor(Cursor c) { int index = -1; return new Builder() @@ -195,8 +175,8 @@ public class SeriesRecording implements Parcelable { } /** - * Returns the ContentValues with keys as the columns specified in {@link SeriesRecordings} - * and the values from {@code r}. + * Returns the ContentValues with keys as the columns specified in {@link SeriesRecordings} and + * the values from {@code r}. */ public static ContentValues toContentValues(SeriesRecording r) { ContentValues values = new ContentValues(); @@ -214,9 +194,9 @@ public class SeriesRecording implements Parcelable { values.put(SeriesRecordings.COLUMN_SERIES_ID, r.getSeriesId()); values.put(SeriesRecordings.COLUMN_START_FROM_EPISODE, r.getStartFromEpisode()); values.put(SeriesRecordings.COLUMN_START_FROM_SEASON, r.getStartFromSeason()); - values.put(SeriesRecordings.COLUMN_CHANNEL_OPTION, - channelOption(r.getChannelOption())); - values.put(SeriesRecordings.COLUMN_CANONICAL_GENRE, + values.put(SeriesRecordings.COLUMN_CHANNEL_OPTION, channelOption(r.getChannelOption())); + values.put( + SeriesRecordings.COLUMN_CANONICAL_GENRE, Utils.getCanonicalGenre(r.getCanonicalGenreIds())); values.put(SeriesRecordings.COLUMN_POSTER_URI, r.getPosterUri()); values.put(SeriesRecordings.COLUMN_PHOTO_URI, r.getPhotoUri()); @@ -234,7 +214,8 @@ public class SeriesRecording implements Parcelable { return SeriesRecordings.OPTION_CHANNEL_ONE; } - @ChannelOption private static int channelOption(String option) { + @ChannelOption + private static int channelOption(String option) { switch (option) { case SeriesRecordings.OPTION_CHANNEL_ONE: return OPTION_CHANNEL_ONE; @@ -254,7 +235,8 @@ public class SeriesRecording implements Parcelable { return SeriesRecordings.STATE_SERIES_NORMAL; } - @SeriesState private static int seriesRecordingState(String state) { + @SeriesState + private static int seriesRecordingState(String state) { switch (state) { case SeriesRecordings.STATE_SERIES_NORMAL: return STATE_SERIES_NORMAL; @@ -264,9 +246,7 @@ public class SeriesRecording implements Parcelable { return STATE_SERIES_NORMAL; } - /** - * Builder for {@link SeriesRecording}. - */ + /** Builder for {@link SeriesRecording}. */ public static class Builder { private long mId = ID_NOT_SET; private long mPriority = DvrScheduleManager.DEFAULT_SERIES_PRIORITY; @@ -284,141 +264,120 @@ public class SeriesRecording implements Parcelable { private String mPhotoUri; private int mState = SeriesRecording.STATE_SERIES_NORMAL; - /** - * @see #getId() - */ + /** @see #getId() */ public Builder setId(long id) { mId = id; return this; } - /** - * @see #getPriority() () - */ + /** @see #getPriority() () */ public Builder setPriority(long priority) { mPriority = priority; return this; } - /** - * @see #getTitle() - */ + /** @see #getTitle() */ public Builder setTitle(String title) { mTitle = title; return this; } - /** - * @see #getDescription() - */ + /** @see #getDescription() */ public Builder setDescription(String description) { mDescription = description; return this; } - /** - * @see #getLongDescription() - */ + /** @see #getLongDescription() */ public Builder setLongDescription(String longDescription) { mLongDescription = longDescription; return this; } - /** - * @see #getInputId() - */ + /** @see #getInputId() */ public Builder setInputId(String inputId) { mInputId = inputId; return this; } - /** - * @see #getChannelId() - */ + /** @see #getChannelId() */ public Builder setChannelId(long channelId) { mChannelId = channelId; return this; } - /** - * @see #getSeriesId() - */ + /** @see #getSeriesId() */ public Builder setSeriesId(String seriesId) { mSeriesId = seriesId; return this; } - /** - * @see #getStartFromSeason() - */ + /** @see #getStartFromSeason() */ public Builder setStartFromSeason(int startFromSeason) { mStartFromSeason = startFromSeason; return this; } - /** - * @see #getChannelOption() - */ + /** @see #getChannelOption() */ public Builder setChannelOption(@ChannelOption int option) { mChannelOption = option; return this; } - /** - * @see #getStartFromEpisode() - */ + /** @see #getStartFromEpisode() */ public Builder setStartFromEpisode(int startFromEpisode) { mStartFromEpisode = startFromEpisode; return this; } - /** - * @see #getCanonicalGenreIds() - */ + /** @see #getCanonicalGenreIds() */ public Builder setCanonicalGenreIds(String genres) { mCanonicalGenreIds = Utils.getCanonicalGenreIds(genres); return this; } - /** - * @see #getCanonicalGenreIds() - */ + /** @see #getCanonicalGenreIds() */ public Builder setCanonicalGenreIds(int[] canonicalGenreIds) { mCanonicalGenreIds = canonicalGenreIds; return this; } - /** - * @see #getPosterUri() - */ + /** @see #getPosterUri() */ public Builder setPosterUri(String posterUri) { mPosterUri = posterUri; return this; } - /** - * @see #getPhotoUri() - */ + /** @see #getPhotoUri() */ public Builder setPhotoUri(String photoUri) { mPhotoUri = photoUri; return this; } - /** - * @see #getState() - */ + /** @see #getState() */ public Builder setState(@SeriesState int state) { mState = state; return this; } - /** - * Creates a new {@link SeriesRecording}. - */ + /** Creates a new {@link SeriesRecording}. */ public SeriesRecording build() { - return new SeriesRecording(mId, mPriority, mTitle, mDescription, mLongDescription, - mInputId, mChannelId, mSeriesId, mStartFromSeason, mStartFromEpisode, - mChannelOption, mCanonicalGenreIds, mPosterUri, mPhotoUri, mState); + return new SeriesRecording( + mId, + mPriority, + mTitle, + mDescription, + mLongDescription, + mInputId, + mChannelId, + mSeriesId, + mStartFromSeason, + mStartFromEpisode, + mChannelOption, + mCanonicalGenreIds, + mPosterUri, + mPhotoUri, + mState); } } @@ -444,16 +403,16 @@ public class SeriesRecording implements Parcelable { public static final Parcelable.Creator<SeriesRecording> CREATOR = new Parcelable.Creator<SeriesRecording>() { - @Override - public SeriesRecording createFromParcel(Parcel in) { - return SeriesRecording.fromParcel(in); - } + @Override + public SeriesRecording createFromParcel(Parcel in) { + return SeriesRecording.fromParcel(in); + } - @Override - public SeriesRecording[] newArray(int size) { - return new SeriesRecording[size]; - } - }; + @Override + public SeriesRecording[] newArray(int size) { + return new SeriesRecording[size]; + } + }; private long mId; private final long mPriority; @@ -471,9 +430,7 @@ public class SeriesRecording implements Parcelable { private final String mPhotoUri; @SeriesState private int mState; - /** - * The input id of this SeriesRecording. - */ + /** The input id of this SeriesRecording. */ public String getInputId() { return mInputId; } @@ -485,16 +442,12 @@ public class SeriesRecording implements Parcelable { return mChannelId; } - /** - * The id of this SeriesRecording. - */ + /** The id of this SeriesRecording. */ public long getId() { return mId; } - /** - * Sets the ID. - */ + /** Sets the ID. */ public void setId(long id) { mId = id; } @@ -502,30 +455,24 @@ public class SeriesRecording implements Parcelable { /** * The priority of this recording. * - * <p> The highest number is recorded first. If there is a tie in mPriority then the higher mId + * <p>The highest number is recorded first. If there is a tie in mPriority then the higher mId * wins. */ public long getPriority() { return mPriority; } - /** - * The series title. - */ + /** The series title. */ public String getTitle() { return mTitle; } - /** - * The series description. - */ + /** The series description. */ public String getDescription() { return mDescription; } - /** - * The long series description. - */ + /** The long series description. */ public String getLongDescription() { return mLongDescription; } @@ -555,44 +502,34 @@ public class SeriesRecording implements Parcelable { return mStartFromSeason; } - /** - * Returns the channel recording option. - */ - @ChannelOption public int getChannelOption() { + /** Returns the channel recording option. */ + @ChannelOption + public int getChannelOption() { return mChannelOption; } - /** - * Returns the canonical genre ID's. - */ + /** Returns the canonical genre ID's. */ public int[] getCanonicalGenreIds() { return mCanonicalGenreIds; } - /** - * Returns the poster URI. - */ + /** Returns the poster URI. */ public String getPosterUri() { return mPosterUri; } - /** - * Returns the photo URI. - */ + /** Returns the photo URI. */ public String getPhotoUri() { return mPhotoUri; } - /** - * Returns the state of series recording. - */ - @SeriesState public int getState() { + /** Returns the state of series recording. */ + @SeriesState + public int getState() { return mState; } - /** - * Checks whether the series recording is stopped or not. - */ + /** Checks whether the series recording is stopped or not. */ public boolean isStopped() { return mState == STATE_SERIES_STOPPED; } @@ -620,35 +557,77 @@ public class SeriesRecording implements Parcelable { @Override public int hashCode() { - return Objects.hash(mPriority, mChannelId, mStartFromSeason, mStartFromEpisode, mId, - mTitle, mDescription, mLongDescription, mSeriesId, mChannelOption, - mCanonicalGenreIds, mPosterUri, mPhotoUri, mState); + return Objects.hash( + mPriority, + mChannelId, + mStartFromSeason, + mStartFromEpisode, + mId, + mTitle, + mDescription, + mLongDescription, + mSeriesId, + mChannelOption, + Arrays.hashCode(mCanonicalGenreIds), + mPosterUri, + mPhotoUri, + mState); } @Override public String toString() { - return "SeriesRecording{" + - "inputId=" + mInputId + - ", channelId=" + mChannelId + - ", id='" + mId + '\'' + - ", priority=" + mPriority + - ", title='" + mTitle + '\'' + - ", description='" + mDescription + '\'' + - ", longDescription='" + mLongDescription + '\'' + - ", startFromSeason=" + mStartFromSeason + - ", startFromEpisode=" + mStartFromEpisode + - ", channelOption=" + mChannelOption + - ", canonicalGenreIds=" + Arrays.toString(mCanonicalGenreIds) + - ", posterUri=" + mPosterUri + - ", photoUri=" + mPhotoUri + - ", state=" + mState + - '}'; - } - - private SeriesRecording(long id, long priority, String title, String description, - String longDescription, String inputId, long channelId, String seriesId, - int startFromSeason, int startFromEpisode, int channelOption, int[] canonicalGenreIds, - String posterUri, String photoUri, int state) { + return "SeriesRecording{" + + "inputId=" + + mInputId + + ", channelId=" + + mChannelId + + ", id='" + + mId + + '\'' + + ", priority=" + + mPriority + + ", title='" + + mTitle + + '\'' + + ", description='" + + mDescription + + '\'' + + ", longDescription='" + + mLongDescription + + '\'' + + ", startFromSeason=" + + mStartFromSeason + + ", startFromEpisode=" + + mStartFromEpisode + + ", channelOption=" + + mChannelOption + + ", canonicalGenreIds=" + + Arrays.toString(mCanonicalGenreIds) + + ", posterUri=" + + mPosterUri + + ", photoUri=" + + mPhotoUri + + ", state=" + + mState + + '}'; + } + + private SeriesRecording( + long id, + long priority, + String title, + String description, + String longDescription, + String inputId, + long channelId, + String seriesId, + int startFromSeason, + int startFromEpisode, + int channelOption, + int[] canonicalGenreIds, + String posterUri, + String photoUri, + int state) { this.mId = id; this.mPriority = priority; this.mTitle = title; @@ -690,9 +669,7 @@ public class SeriesRecording implements Parcelable { out.writeInt(mState); } - /** - * Returns an array containing all of the elements in the list. - */ + /** Returns an array containing all of the elements in the list. */ public static SeriesRecording[] toArray(Collection<SeriesRecording> series) { return series.toArray(new SeriesRecording[series.size()]); } @@ -715,16 +692,16 @@ public class SeriesRecording implements Parcelable { long channelId = program.getChannelId(); String seasonNumber = program.getSeasonNumber(); String episodeNumber = program.getEpisodeNumber(); - if (!mSeriesId.equals(seriesId) || (channelOption == SeriesRecording.OPTION_CHANNEL_ONE - && mChannelId != channelId)) { + if (!mSeriesId.equals(seriesId) + || (channelOption == SeriesRecording.OPTION_CHANNEL_ONE + && mChannelId != channelId)) { return false; } // Season number and episode number matches if // start_season_number < program_season_number // || (start_season_number == program_season_number // && start_episode_number <= program_episode_number). - if (mStartFromSeason == SeriesRecordings.THE_BEGINNING - || TextUtils.isEmpty(seasonNumber)) { + if (mStartFromSeason == SeriesRecordings.THE_BEGINNING || TextUtils.isEmpty(seasonNumber)) { return true; } else { int intSeasonNumber; diff --git a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java index c5383d02..7d2af9c3 100644 --- a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java +++ b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java @@ -20,27 +20,23 @@ import android.content.Context; import android.database.Cursor; import android.os.AsyncTask; import android.support.annotation.Nullable; - +import com.android.tv.common.concurrent.NamedThreadFactory; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.provider.DvrContract.Schedules; import com.android.tv.dvr.provider.DvrContract.SeriesRecordings; -import com.android.tv.util.NamedThreadFactory; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -/** - * {@link AsyncTask} that defaults to executing on its own single threaded Executor Service. - */ +/** {@link AsyncTask} that defaults to executing on its own single threaded Executor Service. */ public abstract class AsyncDvrDbTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { - private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory( - AsyncDvrDbTask.class.getSimpleName()); - private static final ExecutorService DB_EXECUTOR = Executors - .newSingleThreadExecutor(THREAD_FACTORY); + private static final NamedThreadFactory THREAD_FACTORY = + new NamedThreadFactory(AsyncDvrDbTask.class.getSimpleName()); + private static final ExecutorService DB_EXECUTOR = + Executors.newSingleThreadExecutor(THREAD_FACTORY); private static DvrDatabaseHelper sDbHelper; @@ -57,9 +53,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> mContext = context; } - /** - * Execute the task on the {@link #DB_EXECUTOR} thread. - */ + /** Execute the task on the {@link #DB_EXECUTOR} thread. */ @SafeVarargs public final void executeOnDbThread(Params... params) { executeOnExecutor(DB_EXECUTOR, params); @@ -71,15 +65,11 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> return doInDvrBackground(params); } - /** - * Executes in the background after {@link #initializeDbHelper(Context)} - */ + /** Executes in the background after {@link #initializeDbHelper(Context)} */ @Nullable protected abstract Result doInDvrBackground(Params... params); - /** - * Inserts schedules. - */ + /** Inserts schedules. */ public static class AsyncAddScheduleTask extends AsyncDvrDbTask<ScheduledRecording, Void, Void> { public AsyncAddScheduleTask(Context context) { @@ -93,9 +83,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Update schedules. - */ + /** Update schedules. */ public static class AsyncUpdateScheduleTask extends AsyncDvrDbTask<ScheduledRecording, Void, Void> { public AsyncUpdateScheduleTask(Context context) { @@ -109,9 +97,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Delete schedules. - */ + /** Delete schedules. */ public static class AsyncDeleteScheduleTask extends AsyncDvrDbTask<ScheduledRecording, Void, Void> { public AsyncDeleteScheduleTask(Context context) { @@ -125,9 +111,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Returns all {@link ScheduledRecording}s. - */ + /** Returns all {@link ScheduledRecording}s. */ public abstract static class AsyncDvrQueryScheduleTask extends AsyncDvrDbTask<Void, Void, List<ScheduledRecording>> { public AsyncDvrQueryScheduleTask(Context context) { @@ -150,9 +134,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Inserts series recordings. - */ + /** Inserts series recordings. */ public static class AsyncAddSeriesRecordingTask extends AsyncDvrDbTask<SeriesRecording, Void, Void> { public AsyncAddSeriesRecordingTask(Context context) { @@ -166,9 +148,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Update series recordings. - */ + /** Update series recordings. */ public static class AsyncUpdateSeriesRecordingTask extends AsyncDvrDbTask<SeriesRecording, Void, Void> { public AsyncUpdateSeriesRecordingTask(Context context) { @@ -182,9 +162,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Delete series recordings. - */ + /** Delete series recordings. */ public static class AsyncDeleteSeriesRecordingTask extends AsyncDvrDbTask<SeriesRecording, Void, Void> { public AsyncDeleteSeriesRecordingTask(Context context) { @@ -198,9 +176,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> } } - /** - * Returns all {@link SeriesRecording}s. - */ + /** Returns all {@link SeriesRecording}s. */ public abstract static class AsyncDvrQuerySeriesRecordingTask extends AsyncDvrDbTask<Void, Void, List<SeriesRecording>> { public AsyncDvrQuerySeriesRecordingTask(Context context) { @@ -214,8 +190,8 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result> return null; } List<SeriesRecording> scheduledRecordings = new ArrayList<>(); - try (Cursor c = sDbHelper.query(SeriesRecordings.TABLE_NAME, - SeriesRecording.PROJECTION)) { + try (Cursor c = + sDbHelper.query(SeriesRecordings.TABLE_NAME, SeriesRecording.PROJECTION)) { while (c.moveToNext() && !isCancelled()) { scheduledRecordings.add(SeriesRecording.fromCursor(c)); } diff --git a/src/com/android/tv/dvr/provider/DvrContract.java b/src/com/android/tv/dvr/provider/DvrContract.java index f0aca18e..a5f2e2cd 100644 --- a/src/com/android/tv/dvr/provider/DvrContract.java +++ b/src/com/android/tv/dvr/provider/DvrContract.java @@ -55,11 +55,57 @@ public final class DvrContract { /** The recording marked as canceled. */ public static final String STATE_RECORDING_CANCELED = "STATE_RECORDING_CANCELED"; + /** The recording failed reason for other reasons */ + public static final String FAILED_REASON_OTHER = "FAILED_REASON_OTHER"; + + /** The recording failed because the program ended before recording started. */ + public static final String FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED = + "FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED"; + + /** The recording failed because it was not finished successfully */ + public static final String FAILED_REASON_NOT_FINISHED = "FAILED_REASON_NOT_FINISHED"; + + /** The recording failed because the channel ID was invalid */ + public static final String FAILED_REASON_INVALID_CHANNEL = "FAILED_REASON_INVALID_CHANNEL"; + + /** The recording failed because the scheduler was stopped */ + public static final String FAILED_REASON_SCHEDULER_STOPPED + = "FAILED_REASON_SCHEDULER_STOPPED"; + + /** The recording failed because some messages were not sent to the message queue */ + public static final String FAILED_REASON_MESSAGE_NOT_SENT = + "FAILED_REASON_MESSAGE_NOT_SENT"; + + /** + * The recording failed because it was failed to establish a connection to the recording + * session for the corresponding TV input. + */ + public static final String FAILED_REASON_CONNECTION_FAILED = + "FAILED_REASON_CONNECTION_FAILED"; + + /** + * The recording failed because a required recording resource was not able to be + * allocated. + */ + public static final String FAILED_REASON_RESOURCE_BUSY = "FAILED_REASON_RESOURCE_BUSY"; + + /** The recording failed because the input was not available */ + public static final String FAILED_REASON_INPUT_UNAVAILABLE = + "FAILED_REASON_INPUT_UNAVAILABLE"; + + /** The recording failed because the input doesn't support recording */ + public static final String FAILED_REASON_INPUT_DVR_UNSUPPORTED = + "FAILED_REASON_INPUT_DVR_UNSUPPORTED"; + + /** The recording failed because the space was not sufficient */ + public static final String FAILED_REASON_INSUFFICIENT_SPACE = + "FAILED_REASON_INSUFFICIENT_SPACE"; + /** * The priority of this recording. * - * <p> The lowest number is recorded first. If there is a tie in priority then the lower id - * wins. Defaults to {@value Long#MAX_VALUE} + * <p>The lowest number is recorded first. If there is a tie in priority then the lower id + * wins. Defaults to {@value Long#MAX_VALUE} * * <p>Type: INTEGER (long) */ @@ -68,8 +114,8 @@ public final class DvrContract { /** * The type of this recording. * - * <p>This value should be one of the followings: {@link #TYPE_PROGRAM} and - * {@link #TYPE_TIMED}. + * <p>This value should be one of the followings: {@link #TYPE_PROGRAM} and {@link + * #TYPE_TIMED}. * * <p>This is a required field. * @@ -184,9 +230,9 @@ public final class DvrContract { * The state of this recording. * * <p>This value should be one of the followings: {@link #STATE_RECORDING_NOT_STARTED}, - * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, - * {@link #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and - * {@link #STATE_RECORDING_DELETED}. + * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, {@link + * #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and {@link + * #STATE_RECORDING_DELETED}. * * <p>This is a required field. * @@ -195,13 +241,20 @@ public final class DvrContract { public static final String COLUMN_STATE = "state"; /** + * The reason of failure of this recording if it's failed. + * + * <p>Type: TEXT + */ + public static final String COLUMN_FAILED_REASON = "failed_reason"; + + /** * The ID of the parent series recording. * * <p>Type: INTEGER (long) */ public static final String COLUMN_SERIES_RECORDING_ID = "series_recording_id"; - private Schedules() { } + private Schedules() {} } /** Column definition for Recording table. */ @@ -210,8 +263,8 @@ public final class DvrContract { public static final String TABLE_NAME = "series_recording"; /** - * This value is used for {@link #COLUMN_START_FROM_SEASON} and - * {@link #COLUMN_START_FROM_EPISODE} to mean record all seasons or episodes. + * This value is used for {@link #COLUMN_START_FROM_SEASON} and {@link + * #COLUMN_START_FROM_EPISODE} to mean record all seasons or episodes. */ public static final int THE_BEGINNING = -1; @@ -227,21 +280,17 @@ public final class DvrContract { */ public static final String OPTION_CHANNEL_ALL = "OPTION_CHANNEL_ALL"; - /** - * The state indicates that it is a normal one. - */ + /** The state indicates that it is a normal one. */ public static final String STATE_SERIES_NORMAL = "STATE_SERIES_NORMAL"; - /** - * The state indicates that it is stopped. - */ + /** The state indicates that it is stopped. */ public static final String STATE_SERIES_STOPPED = "STATE_SERIES_STOPPED"; /** * The priority of this recording. * - * <p> The lowest number is recorded first. If there is a tie in priority then the lower id - * wins. Defaults to {@value Long#MAX_VALUE} + * <p>The lowest number is recorded first. If there is a tie in priority then the lower id + * wins. Defaults to {@value Long#MAX_VALUE} * * <p>Type: INTEGER (long) */ @@ -266,7 +315,7 @@ public final class DvrContract { public static final String COLUMN_CHANNEL_ID = "channel_id"; /** - * The ID of the associated series to record. + * The ID of the associated series to record. * * <p>The id is an opaque but stable string. * @@ -300,8 +349,8 @@ public final class DvrContract { public static final String COLUMN_LONG_DESCRIPTION = "long_description"; /** - * The number of the earliest season to record. The - * value {@link #THE_BEGINNING} means record all seasons. + * The number of the earliest season to record. The value {@link #THE_BEGINNING} means + * record all seasons. * * <p>Default value is {@value #THE_BEGINNING} {@link #THE_BEGINNING}. * @@ -310,7 +359,7 @@ public final class DvrContract { public static final String COLUMN_START_FROM_SEASON = "start_from_season"; /** - * The number of the earliest episode to record in {@link #COLUMN_START_FROM_SEASON}. The + * The number of the earliest episode to record in {@link #COLUMN_START_FROM_SEASON}. The * value {@link #THE_BEGINNING} means record all episodes. * * <p>Default value is {@value #THE_BEGINNING} {@link #THE_BEGINNING}. @@ -322,8 +371,8 @@ public final class DvrContract { /** * The series recording option which indicates the channels to record. * - * <p>This value should be one of the followings: {@link #OPTION_CHANNEL_ONE} and - * {@link #OPTION_CHANNEL_ALL}. The default value is OPTION_CHANNEL_ONE. + * <p>This value should be one of the followings: {@link #OPTION_CHANNEL_ONE} and {@link + * #OPTION_CHANNEL_ALL}. The default value is OPTION_CHANNEL_ONE. * * <p>Type: TEXT */ @@ -338,6 +387,7 @@ public final class DvrContract { * to get the canonical genre strings from the text stored in the column. * * <p>Type: TEXT + * * @see android.media.tv.TvContract.Programs.Genres * @see android.media.tv.TvContract.Programs.Genres#encode * @see android.media.tv.TvContract.Programs.Genres#decode @@ -350,10 +400,9 @@ public final class DvrContract { * <p>The data in the column must be a URL, or a URI in one of the following formats: * * <ul> - * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> - * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) - * </li> - * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT}) + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE}) * </ul> * * <p>Type: TEXT @@ -366,10 +415,9 @@ public final class DvrContract { * <p>The data in the column must be a URL, or a URI in one of the following formats: * * <ul> - * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> - * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) - * </li> - * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT}) + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE}) * </ul> * * <p>Type: TEXT @@ -379,15 +427,15 @@ public final class DvrContract { /** * The state of whether the series recording be canceled or not. * - * <p>This value should be one of the followings: {@link #STATE_SERIES_NORMAL} and - * {@link #STATE_SERIES_STOPPED}. The default value is STATE_SERIES_NORMAL. + * <p>This value should be one of the followings: {@link #STATE_SERIES_NORMAL} and {@link + * #STATE_SERIES_STOPPED}. The default value is STATE_SERIES_NORMAL. * * <p>Type: TEXT */ public static final String COLUMN_STATE = "state"; - private SeriesRecordings() { } + private SeriesRecordings() {} } - private DvrContract() { } + private DvrContract() {} } diff --git a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java index 8b9481a9..41e5a66a 100644 --- a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java +++ b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java @@ -26,98 +26,145 @@ import android.database.sqlite.SQLiteStatement; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; - import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.provider.DvrContract.Schedules; import com.android.tv.dvr.provider.DvrContract.SeriesRecordings; -/** - * A data class for one recorded contents. - */ +/** A data class for one recorded contents. */ public class DvrDatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "DvrDatabaseHelper"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; - private static final int DATABASE_VERSION = 17; + private static final int DATABASE_VERSION = 18; private static final String DB_NAME = "dvr.db"; private static final String SQL_CREATE_SCHEDULES = - "CREATE TABLE " + Schedules.TABLE_NAME + "(" - + Schedules._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + Schedules.COLUMN_PRIORITY + " INTEGER DEFAULT " - + ScheduledRecording.DEFAULT_PRIORITY + "," - + Schedules.COLUMN_TYPE + " TEXT NOT NULL," - + Schedules.COLUMN_INPUT_ID + " TEXT NOT NULL," - + Schedules.COLUMN_CHANNEL_ID + " INTEGER NOT NULL," - + Schedules.COLUMN_PROGRAM_ID + " INTEGER," - + Schedules.COLUMN_PROGRAM_TITLE + " TEXT," - + Schedules.COLUMN_START_TIME_UTC_MILLIS + " INTEGER NOT NULL," - + Schedules.COLUMN_END_TIME_UTC_MILLIS + " INTEGER NOT NULL," - + Schedules.COLUMN_SEASON_NUMBER + " TEXT," - + Schedules.COLUMN_EPISODE_NUMBER + " TEXT," - + Schedules.COLUMN_EPISODE_TITLE + " TEXT," - + Schedules.COLUMN_PROGRAM_DESCRIPTION + " TEXT," - + Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION + " TEXT," - + Schedules.COLUMN_PROGRAM_POST_ART_URI + " TEXT," - + Schedules.COLUMN_PROGRAM_THUMBNAIL_URI + " TEXT," - + Schedules.COLUMN_STATE + " TEXT NOT NULL," - + Schedules.COLUMN_SERIES_RECORDING_ID + " INTEGER," - + "FOREIGN KEY(" + Schedules.COLUMN_SERIES_RECORDING_ID + ") " - + "REFERENCES " + SeriesRecordings.TABLE_NAME - + "(" + SeriesRecordings._ID + ") " + "CREATE TABLE " + + Schedules.TABLE_NAME + + "(" + + Schedules._ID + + " INTEGER PRIMARY KEY AUTOINCREMENT," + + Schedules.COLUMN_PRIORITY + + " INTEGER DEFAULT " + + ScheduledRecording.DEFAULT_PRIORITY + + "," + + Schedules.COLUMN_TYPE + + " TEXT NOT NULL," + + Schedules.COLUMN_INPUT_ID + + " TEXT NOT NULL," + + Schedules.COLUMN_CHANNEL_ID + + " INTEGER NOT NULL," + + Schedules.COLUMN_PROGRAM_ID + + " INTEGER," + + Schedules.COLUMN_PROGRAM_TITLE + + " TEXT," + + Schedules.COLUMN_START_TIME_UTC_MILLIS + + " INTEGER NOT NULL," + + Schedules.COLUMN_END_TIME_UTC_MILLIS + + " INTEGER NOT NULL," + + Schedules.COLUMN_SEASON_NUMBER + + " TEXT," + + Schedules.COLUMN_EPISODE_NUMBER + + " TEXT," + + Schedules.COLUMN_EPISODE_TITLE + + " TEXT," + + Schedules.COLUMN_PROGRAM_DESCRIPTION + + " TEXT," + + Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION + + " TEXT," + + Schedules.COLUMN_PROGRAM_POST_ART_URI + + " TEXT," + + Schedules.COLUMN_PROGRAM_THUMBNAIL_URI + + " TEXT," + + Schedules.COLUMN_STATE + + " TEXT NOT NULL," + + Schedules.COLUMN_SERIES_RECORDING_ID + + " INTEGER," + + "FOREIGN KEY(" + + Schedules.COLUMN_SERIES_RECORDING_ID + + ") " + + "REFERENCES " + + SeriesRecordings.TABLE_NAME + + "(" + + SeriesRecordings._ID + + ") " + "ON UPDATE CASCADE ON DELETE SET NULL);"; private static final String SQL_DROP_SCHEDULES = "DROP TABLE IF EXISTS " + Schedules.TABLE_NAME; private static final String SQL_CREATE_SERIES_RECORDINGS = - "CREATE TABLE " + SeriesRecordings.TABLE_NAME + "(" - + SeriesRecordings._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + SeriesRecordings.COLUMN_PRIORITY + " INTEGER DEFAULT " - + SeriesRecording.DEFAULT_PRIORITY + "," - + SeriesRecordings.COLUMN_TITLE + " TEXT NOT NULL," - + SeriesRecordings.COLUMN_SHORT_DESCRIPTION + " TEXT," - + SeriesRecordings.COLUMN_LONG_DESCRIPTION + " TEXT," - + SeriesRecordings.COLUMN_INPUT_ID + " TEXT NOT NULL," - + SeriesRecordings.COLUMN_CHANNEL_ID + " INTEGER NOT NULL," - + SeriesRecordings.COLUMN_SERIES_ID + " TEXT NOT NULL," - + SeriesRecordings.COLUMN_START_FROM_SEASON + " INTEGER DEFAULT " - + SeriesRecordings.THE_BEGINNING + "," - + SeriesRecordings.COLUMN_START_FROM_EPISODE + " INTEGER DEFAULT " - + SeriesRecordings.THE_BEGINNING + "," - + SeriesRecordings.COLUMN_CHANNEL_OPTION + " TEXT DEFAULT " - + SeriesRecordings.OPTION_CHANNEL_ONE + "," - + SeriesRecordings.COLUMN_CANONICAL_GENRE + " TEXT," - + SeriesRecordings.COLUMN_POSTER_URI + " TEXT," - + SeriesRecordings.COLUMN_PHOTO_URI + " TEXT," - + SeriesRecordings.COLUMN_STATE + " TEXT)"; + "CREATE TABLE " + + SeriesRecordings.TABLE_NAME + + "(" + + SeriesRecordings._ID + + " INTEGER PRIMARY KEY AUTOINCREMENT," + + SeriesRecordings.COLUMN_PRIORITY + + " INTEGER DEFAULT " + + SeriesRecording.DEFAULT_PRIORITY + + "," + + SeriesRecordings.COLUMN_TITLE + + " TEXT NOT NULL," + + SeriesRecordings.COLUMN_SHORT_DESCRIPTION + + " TEXT," + + SeriesRecordings.COLUMN_LONG_DESCRIPTION + + " TEXT," + + SeriesRecordings.COLUMN_INPUT_ID + + " TEXT NOT NULL," + + SeriesRecordings.COLUMN_CHANNEL_ID + + " INTEGER NOT NULL," + + SeriesRecordings.COLUMN_SERIES_ID + + " TEXT NOT NULL," + + SeriesRecordings.COLUMN_START_FROM_SEASON + + " INTEGER DEFAULT " + + SeriesRecordings.THE_BEGINNING + + "," + + SeriesRecordings.COLUMN_START_FROM_EPISODE + + " INTEGER DEFAULT " + + SeriesRecordings.THE_BEGINNING + + "," + + SeriesRecordings.COLUMN_CHANNEL_OPTION + + " TEXT DEFAULT " + + SeriesRecordings.OPTION_CHANNEL_ONE + + "," + + SeriesRecordings.COLUMN_CANONICAL_GENRE + + " TEXT," + + SeriesRecordings.COLUMN_POSTER_URI + + " TEXT," + + SeriesRecordings.COLUMN_PHOTO_URI + + " TEXT," + + SeriesRecordings.COLUMN_STATE + + " TEXT)"; - private static final String SQL_DROP_SERIES_RECORDINGS = "DROP TABLE IF EXISTS " + - SeriesRecordings.TABLE_NAME; + private static final String SQL_DROP_SERIES_RECORDINGS = + "DROP TABLE IF EXISTS " + SeriesRecordings.TABLE_NAME; private static final int SQL_DATA_TYPE_LONG = 0; private static final int SQL_DATA_TYPE_INT = 1; private static final int SQL_DATA_TYPE_STRING = 2; - private static final ColumnInfo[] COLUMNS_SCHEDULES = new ColumnInfo[] { - new ColumnInfo(Schedules._ID, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_TYPE, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_PROGRAM_ID, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_PROGRAM_TITLE, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_START_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_END_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG), - new ColumnInfo(Schedules.COLUMN_SEASON_NUMBER, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_EPISODE_NUMBER, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_EPISODE_TITLE, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_PROGRAM_DESCRIPTION, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_PROGRAM_POST_ART_URI, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_STATE, SQL_DATA_TYPE_STRING), - new ColumnInfo(Schedules.COLUMN_SERIES_RECORDING_ID, SQL_DATA_TYPE_LONG)}; + private static final ColumnInfo[] COLUMNS_SCHEDULES = + new ColumnInfo[] { + new ColumnInfo(Schedules._ID, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_TYPE, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_PROGRAM_ID, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_PROGRAM_TITLE, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_START_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_END_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG), + new ColumnInfo(Schedules.COLUMN_SEASON_NUMBER, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_EPISODE_NUMBER, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_EPISODE_TITLE, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_PROGRAM_DESCRIPTION, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_PROGRAM_POST_ART_URI, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_STATE, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_FAILED_REASON, SQL_DATA_TYPE_STRING), + new ColumnInfo(Schedules.COLUMN_SERIES_RECORDING_ID, SQL_DATA_TYPE_LONG) + }; private static final String SQL_INSERT_SCHEDULES = buildInsertSql(Schedules.TABLE_NAME, COLUMNS_SCHEDULES); @@ -125,22 +172,24 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { buildUpdateSql(Schedules.TABLE_NAME, COLUMNS_SCHEDULES); private static final String SQL_DELETE_SCHEDULES = buildDeleteSql(Schedules.TABLE_NAME); - private static final ColumnInfo[] COLUMNS_SERIES_RECORDINGS = new ColumnInfo[] { - new ColumnInfo(SeriesRecordings._ID, SQL_DATA_TYPE_LONG), - new ColumnInfo(SeriesRecordings.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG), - new ColumnInfo(SeriesRecordings.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG), - new ColumnInfo(SeriesRecordings.COLUMN_SERIES_ID, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_TITLE, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_SHORT_DESCRIPTION, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_SEASON, SQL_DATA_TYPE_INT), - new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_EPISODE, SQL_DATA_TYPE_INT), - new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_OPTION, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_CANONICAL_GENRE, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_POSTER_URI, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_PHOTO_URI, SQL_DATA_TYPE_STRING), - new ColumnInfo(SeriesRecordings.COLUMN_STATE, SQL_DATA_TYPE_STRING)}; + private static final ColumnInfo[] COLUMNS_SERIES_RECORDINGS = + new ColumnInfo[] { + new ColumnInfo(SeriesRecordings._ID, SQL_DATA_TYPE_LONG), + new ColumnInfo(SeriesRecordings.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG), + new ColumnInfo(SeriesRecordings.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG), + new ColumnInfo(SeriesRecordings.COLUMN_SERIES_ID, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_TITLE, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_SHORT_DESCRIPTION, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_SEASON, SQL_DATA_TYPE_INT), + new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_EPISODE, SQL_DATA_TYPE_INT), + new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_OPTION, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_CANONICAL_GENRE, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_POSTER_URI, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_PHOTO_URI, SQL_DATA_TYPE_STRING), + new ColumnInfo(SeriesRecordings.COLUMN_STATE, SQL_DATA_TYPE_STRING) + }; private static final String SQL_INSERT_SERIES_RECORDINGS = buildInsertSql(SeriesRecordings.TABLE_NAME, COLUMNS_SERIES_RECORDINGS); @@ -186,6 +235,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { private static String buildDeleteSql(String tableName) { return "DELETE FROM " + tableName + " WHERE " + BaseColumns._ID + "=?"; } + public DvrDatabaseHelper(Context context) { super(context.getApplicationContext(), DB_NAME, null, DATABASE_VERSION); } @@ -205,16 +255,20 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SCHEDULES); - db.execSQL(SQL_DROP_SCHEDULES); - if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SERIES_RECORDINGS); - db.execSQL(SQL_DROP_SERIES_RECORDINGS); - onCreate(db); + if (oldVersion < 17) { + if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SCHEDULES); + db.execSQL(SQL_DROP_SCHEDULES); + if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SERIES_RECORDINGS); + db.execSQL(SQL_DROP_SERIES_RECORDINGS); + onCreate(db); + } + if (oldVersion < 18) { + db.execSQL("ALTER TABLE " + Schedules.TABLE_NAME + " ADD COLUMN " + + Schedules.COLUMN_FAILED_REASON + " TEXT DEFAULT null;"); + } } - /** - * Handles the query request and returns a {@link Cursor}. - */ + /** Handles the query request and returns a {@link Cursor}. */ public Cursor query(String tableName, String[] projections) { SQLiteDatabase db = getReadableDatabase(); SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); @@ -222,9 +276,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { return builder.query(db, projections, null, null, null, null, null); } - /** - * Inserts schedules. - */ + /** Inserts schedules. */ public void insertSchedules(ScheduledRecording... scheduledRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_INSERT_SCHEDULES); @@ -242,9 +294,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - /** - * Update schedules. - */ + /** Update schedules. */ public void updateSchedules(ScheduledRecording... scheduledRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_UPDATE_SCHEDULES); @@ -263,9 +313,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - /** - * Delete schedules. - */ + /** Delete schedules. */ public void deleteSchedules(ScheduledRecording... scheduledRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_DELETE_SCHEDULES); @@ -282,9 +330,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - /** - * Inserts series recordings. - */ + /** Inserts series recordings. */ public void insertSeriesRecordings(SeriesRecording... seriesRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_INSERT_SERIES_RECORDINGS); @@ -302,9 +348,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - /** - * Update series recordings. - */ + /** Update series recordings. */ public void updateSeriesRecordings(SeriesRecording... seriesRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_UPDATE_SERIES_RECORDINGS); @@ -323,9 +367,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - /** - * Delete series recordings. - */ + /** Delete series recordings. */ public void deleteSeriesRecordings(SeriesRecording... seriesRecordings) { SQLiteDatabase db = getWritableDatabase(); SQLiteStatement statement = db.compileStatement(SQL_DELETE_SERIES_RECORDINGS); @@ -342,8 +384,8 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { } } - private void bindColumns(SQLiteStatement statement, ColumnInfo[] columns, - ContentValues values) { + private void bindColumns( + SQLiteStatement statement, ColumnInfo[] columns, ContentValues values) { for (int i = 0; i < columns.length; ++i) { ColumnInfo columnInfo = columns[i]; Object value = values.get(columnInfo.name); @@ -362,14 +404,15 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper { statement.bindLong(i + 1, (Integer) value); } break; - case SQL_DATA_TYPE_STRING: { - if (TextUtils.isEmpty((String) value)) { - statement.bindNull(i + 1); - } else { - statement.bindString(i + 1, (String) value); + case SQL_DATA_TYPE_STRING: + { + if (TextUtils.isEmpty((String) value)) { + statement.bindNull(i + 1); + } else { + statement.bindString(i + 1, (String) value); + } + break; } - break; - } } } } diff --git a/src/com/android/tv/dvr/provider/DvrDbSync.java b/src/com/android/tv/dvr/provider/DvrDbSync.java index ff391959..42bc8bcc 100644 --- a/src/com/android/tv/dvr/provider/DvrDbSync.java +++ b/src/com/android/tv/dvr/provider/DvrDbSync.java @@ -29,8 +29,7 @@ import android.os.Looper; import android.support.annotation.MainThread; import android.support.annotation.VisibleForTesting; import android.util.Log; - -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.Program; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; @@ -41,7 +40,6 @@ import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.recorder.SeriesRecordingScheduler; import com.android.tv.util.AsyncDbTask.AsyncQueryProgramTask; import com.android.tv.util.TvUriMatcher; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -50,6 +48,7 @@ import java.util.List; import java.util.Objects; import java.util.Queue; import java.util.Set; +import java.util.concurrent.Executor; /** * A class to synchronizes DVR DB with TvProvider. @@ -70,28 +69,31 @@ public class DvrDbSync { private final DvrManager mDvrManager; private final DvrDataManagerImpl mDataManager; private final ChannelDataManager mChannelDataManager; + private final Executor mDbExecutor; private final Queue<Long> mProgramIdQueue = new LinkedList<>(); private QueryProgramTask mQueryProgramTask; private final SeriesRecordingScheduler mSeriesRecordingScheduler; - private final ContentObserver mContentObserver = new ContentObserver(new Handler( - Looper.getMainLooper())) { - @SuppressLint("SwitchIntDef") - @Override - public void onChange(boolean selfChange, Uri uri) { - switch (TvUriMatcher.match(uri)) { - case TvUriMatcher.MATCH_PROGRAM: - if (DEBUG) Log.d(TAG, "onProgramsUpdated"); - onProgramsUpdated(); - break; - case TvUriMatcher.MATCH_PROGRAM_ID: - if (DEBUG) { - Log.d(TAG, "onProgramUpdated: programId=" + ContentUris.parseId(uri)); + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @SuppressLint("SwitchIntDef") + @Override + public void onChange(boolean selfChange, Uri uri) { + switch (TvUriMatcher.match(uri)) { + case TvUriMatcher.MATCH_PROGRAM: + if (DEBUG) Log.d(TAG, "onProgramsUpdated"); + onProgramsUpdated(); + break; + case TvUriMatcher.MATCH_PROGRAM_ID: + if (DEBUG) { + Log.d( + TAG, + "onProgramUpdated: programId=" + ContentUris.parseId(uri)); + } + onProgramUpdated(ContentUris.parseId(uri)); + break; } - onProgramUpdated(ContentUris.parseId(uri)); - break; - } - } - }; + } + }; private final ChannelDataManager.Listener mChannelDataManagerListener = new ChannelDataManager.Listener() { @@ -106,70 +108,76 @@ public class DvrDbSync { } @Override - public void onChannelBrowsableChanged() { } + public void onChannelBrowsableChanged() {} }; - private final ScheduledRecordingListener mScheduleListener = new ScheduledRecordingListener() { - @Override - public void onScheduledRecordingAdded(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - addProgramIdToCheckIfNeeded(schedule); - } - startNextUpdateIfNeeded(); - } + private final ScheduledRecordingListener mScheduleListener = + new ScheduledRecordingListener() { + @Override + public void onScheduledRecordingAdded(ScheduledRecording... schedules) { + for (ScheduledRecording schedule : schedules) { + addProgramIdToCheckIfNeeded(schedule); + } + startNextUpdateIfNeeded(); + } - @Override - public void onScheduledRecordingRemoved(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - mProgramIdQueue.remove(schedule.getProgramId()); - } - } + @Override + public void onScheduledRecordingRemoved(ScheduledRecording... schedules) { + for (ScheduledRecording schedule : schedules) { + mProgramIdQueue.remove(schedule.getProgramId()); + } + } - @Override - public void onScheduledRecordingStatusChanged(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - mProgramIdQueue.remove(schedule.getProgramId()); - addProgramIdToCheckIfNeeded(schedule); - } - startNextUpdateIfNeeded(); - } - }; + @Override + public void onScheduledRecordingStatusChanged(ScheduledRecording... schedules) { + for (ScheduledRecording schedule : schedules) { + mProgramIdQueue.remove(schedule.getProgramId()); + addProgramIdToCheckIfNeeded(schedule); + } + startNextUpdateIfNeeded(); + } + }; public DvrDbSync(Context context, DvrDataManagerImpl dataManager) { - this(context, dataManager, TvApplication.getSingletons(context).getChannelDataManager(), - TvApplication.getSingletons(context).getDvrManager(), - SeriesRecordingScheduler.getInstance(context)); + this( + context, + dataManager, + TvSingletons.getSingletons(context).getChannelDataManager(), + TvSingletons.getSingletons(context).getDvrManager(), + SeriesRecordingScheduler.getInstance(context), + TvSingletons.getSingletons(context).getDbExecutor()); } @VisibleForTesting - DvrDbSync(Context context, DvrDataManagerImpl dataManager, - ChannelDataManager channelDataManager, DvrManager dvrManager, - SeriesRecordingScheduler seriesRecordingScheduler) { + DvrDbSync( + Context context, + DvrDataManagerImpl dataManager, + ChannelDataManager channelDataManager, + DvrManager dvrManager, + SeriesRecordingScheduler seriesRecordingScheduler, + Executor dbExecutor) { mContext = context; mDvrManager = dvrManager; mDataManager = dataManager; mChannelDataManager = channelDataManager; mSeriesRecordingScheduler = seriesRecordingScheduler; + mDbExecutor = dbExecutor; } - /** - * Starts the DB sync. - */ + /** Starts the DB sync. */ public void start() { if (!mChannelDataManager.isDbLoadFinished()) { mChannelDataManager.addListener(mChannelDataManagerListener); return; } - mContext.getContentResolver().registerContentObserver(Programs.CONTENT_URI, true, - mContentObserver); + mContext.getContentResolver() + .registerContentObserver(Programs.CONTENT_URI, true, mContentObserver); mDataManager.addScheduledRecordingListener(mScheduleListener); onChannelsUpdated(); onProgramsUpdated(); } - /** - * Stops the DB sync. - */ + /** Stops the DB sync. */ public void stop() { mProgramIdQueue.clear(); if (mQueryProgramTask != null) { @@ -185,14 +193,15 @@ public class DvrDbSync { for (SeriesRecording r : mDataManager.getSeriesRecordings()) { if (r.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE && !mChannelDataManager.doesChannelExistInDb(r.getChannelId())) { - seriesRecordingsToUpdate.add(SeriesRecording.buildFrom(r) - .setChannelOption(SeriesRecording.OPTION_CHANNEL_ALL) - .setState(SeriesRecording.STATE_SERIES_STOPPED).build()); + seriesRecordingsToUpdate.add( + SeriesRecording.buildFrom(r) + .setChannelOption(SeriesRecording.OPTION_CHANNEL_ALL) + .setState(SeriesRecording.STATE_SERIES_STOPPED) + .build()); } } if (!seriesRecordingsToUpdate.isEmpty()) { - mDataManager.updateSeriesRecording( - SeriesRecording.toArray(seriesRecordingsToUpdate)); + mDataManager.updateSeriesRecording(SeriesRecording.toArray(seriesRecordingsToUpdate)); } List<ScheduledRecording> schedulesToRemove = new ArrayList<>(); for (ScheduledRecording r : mDataManager.getAvailableScheduledRecordings()) { @@ -202,8 +211,7 @@ public class DvrDbSync { } } if (!schedulesToRemove.isEmpty()) { - mDataManager.removeScheduledRecording( - ScheduledRecording.toArray(schedulesToRemove)); + mDataManager.removeScheduledRecording(ScheduledRecording.toArray(schedulesToRemove)); } } @@ -227,7 +235,7 @@ public class DvrDbSync { if (programId != ScheduledRecording.ID_NOT_SET && !mProgramIdQueue.contains(programId) && (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { + || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { if (DEBUG) Log.d(TAG, "Program ID enqueued: " + programId); mProgramIdQueue.offer(programId); // There are schedules to be updated. Pause the SeriesRecordingScheduler until all the @@ -258,7 +266,7 @@ public class DvrDbSync { ScheduledRecording schedule = mDataManager.getScheduledRecordingForProgramId(programId); if (schedule != null && (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { + || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { if (program == null) { mDataManager.removeScheduledRecording(schedule); if (schedule.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET) { @@ -270,15 +278,16 @@ public class DvrDbSync { } } else { long currentTimeMs = System.currentTimeMillis(); - ScheduledRecording.Builder builder = ScheduledRecording.buildFrom(schedule) - .setEndTimeMs(program.getEndTimeUtcMillis()) - .setSeasonNumber(program.getSeasonNumber()) - .setEpisodeNumber(program.getEpisodeNumber()) - .setEpisodeTitle(program.getEpisodeTitle()) - .setProgramDescription(program.getDescription()) - .setProgramLongDescription(program.getLongDescription()) - .setProgramPosterArtUri(program.getPosterArtUri()) - .setProgramThumbnailUri(program.getThumbnailUri()); + ScheduledRecording.Builder builder = + ScheduledRecording.buildFrom(schedule) + .setEndTimeMs(program.getEndTimeUtcMillis()) + .setSeasonNumber(program.getSeasonNumber()) + .setEpisodeNumber(program.getEpisodeNumber()) + .setEpisodeTitle(program.getEpisodeTitle()) + .setProgramDescription(program.getDescription()) + .setProgramLongDescription(program.getLongDescription()) + .setProgramPosterArtUri(program.getPosterArtUri()) + .setProgramThumbnailUri(program.getThumbnailUri()); boolean needUpdate = false; // Check the series recording. SeriesRecording seriesRecordingForOldSchedule = @@ -289,9 +298,11 @@ public class DvrDbSync { mDataManager.getSeriesRecording(program.getSeriesId()); if (seriesRecording == null) { // The new program is episodic while the previous one isn't. - SeriesRecording newSeriesRecording = mDvrManager.addSeriesRecording( - program, Collections.singletonList(program), - SeriesRecording.STATE_SERIES_STOPPED); + SeriesRecording newSeriesRecording = + mDvrManager.addSeriesRecording( + program, + Collections.singletonList(program), + SeriesRecording.STATE_SERIES_STOPPED); builder.setSeriesRecordingId(newSeriesRecording.getId()); needUpdate = true; } else if (seriesRecording.getId() != schedule.getSeriesRecordingId()) { @@ -302,10 +313,10 @@ public class DvrDbSync { if (seriesRecordingForOldSchedule != null) { seriesRecordingsToUpdate.add(seriesRecordingForOldSchedule); } - } else if (!Objects.equals(schedule.getSeasonNumber(), - program.getSeasonNumber()) - || !Objects.equals(schedule.getEpisodeNumber(), - program.getEpisodeNumber())) { + } else if (!Objects.equals( + schedule.getSeasonNumber(), program.getSeasonNumber()) + || !Objects.equals( + schedule.getEpisodeNumber(), program.getEpisodeNumber())) { // The episode number has been changed. if (seriesRecordingForOldSchedule != null) { seriesRecordingsToUpdate.add(seriesRecordingForOldSchedule); @@ -318,23 +329,24 @@ public class DvrDbSync { // Change start time only when the recording is not started yet. boolean needToChangeStartTime = schedule.getState() != ScheduledRecording.STATE_RECORDING_IN_PROGRESS - && program.getStartTimeUtcMillis() != schedule.getStartTimeMs(); + && program.getStartTimeUtcMillis() != schedule.getStartTimeMs(); if (needToChangeStartTime) { builder.setStartTimeMs(program.getStartTimeUtcMillis()); needUpdate = true; } - if (needUpdate || schedule.getEndTimeMs() != program.getEndTimeUtcMillis() + if (needUpdate + || schedule.getEndTimeMs() != program.getEndTimeUtcMillis() || !Objects.equals(schedule.getSeasonNumber(), program.getSeasonNumber()) || !Objects.equals(schedule.getEpisodeNumber(), program.getEpisodeNumber()) || !Objects.equals(schedule.getEpisodeTitle(), program.getEpisodeTitle()) - || !Objects.equals(schedule.getProgramDescription(), - program.getDescription()) - || !Objects.equals(schedule.getProgramLongDescription(), - program.getLongDescription()) - || !Objects.equals(schedule.getProgramPosterArtUri(), - program.getPosterArtUri()) - || !Objects.equals(schedule.getProgramThumbnailUri(), - program.getThumbnailUri())) { + || !Objects.equals( + schedule.getProgramDescription(), program.getDescription()) + || !Objects.equals( + schedule.getProgramLongDescription(), program.getLongDescription()) + || !Objects.equals( + schedule.getProgramPosterArtUri(), program.getPosterArtUri()) + || !Objects.equals( + schedule.getProgramThumbnailUri(), program.getThumbnailUri())) { mDataManager.updateScheduledRecording(builder.build()); } if (!seriesRecordingsToUpdate.isEmpty()) { @@ -349,7 +361,7 @@ public class DvrDbSync { private final long mProgramId; QueryProgramTask(long programId) { - super(mContext.getContentResolver(), programId); + super(mDbExecutor, mContext.getContentResolver(), programId); mProgramId = programId; } diff --git a/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java b/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java index ba0aca51..b7d9f3b3 100644 --- a/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java +++ b/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java @@ -25,18 +25,16 @@ import android.net.Uri; import android.os.Build; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; - -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; +import com.android.tv.common.util.PermissionUtils; import com.android.tv.data.Program; import com.android.tv.dvr.DvrDataManager; -import com.android.tv.dvr.data.SeasonEpisodeNumber; import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.data.SeasonEpisodeNumber; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.util.AsyncDbTask.AsyncProgramQueryTask; import com.android.tv.util.AsyncDbTask.CursorFilter; -import com.android.tv.util.PermissionUtils; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,11 +42,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -/** - * A wrapper of AsyncProgramQueryTask to load the episodic programs for the series recordings. - */ +/** A wrapper of AsyncProgramQueryTask to load the episodic programs for the series recordings. */ @TargetApi(Build.VERSION_CODES.N) -abstract public class EpisodicProgramLoadTask { +public abstract class EpisodicProgramLoadTask { private static final String TAG = "EpisodicProgramLoadTask"; private static final int PROGRAM_ID_INDEX = Program.getColumnIndex(Programs._ID); @@ -61,11 +57,15 @@ abstract public class EpisodicProgramLoadTask { private static final String PARAM_END_TIME = "end_time"; private static final String PROGRAM_PREDICATE = - Programs.COLUMN_START_TIME_UTC_MILLIS + ">? AND " - + Programs.COLUMN_RECORDING_PROHIBITED + "=0"; + Programs.COLUMN_START_TIME_UTC_MILLIS + + ">? AND " + + Programs.COLUMN_RECORDING_PROHIBITED + + "=0"; private static final String PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM = - Programs.COLUMN_END_TIME_UTC_MILLIS + ">? AND " - + Programs.COLUMN_RECORDING_PROHIBITED + "=0"; + Programs.COLUMN_END_TIME_UTC_MILLIS + + ">? AND " + + Programs.COLUMN_RECORDING_PROHIBITED + + "=0"; private static final String CHANNEL_ID_PREDICATE = Programs.COLUMN_CHANNEL_ID + "=?"; private static final String PROGRAM_TITLE_PREDICATE = Programs.COLUMN_TITLE + "=?"; @@ -80,10 +80,7 @@ abstract public class EpisodicProgramLoadTask { private final ArrayList<SeriesRecording> mSeriesRecordings = new ArrayList<>(); private AsyncProgramQueryTask mProgramQueryTask; - /** - * - * Constructor used to load programs for one series recording with the given channel option. - */ + /** Constructor used to load programs for one series recording with the given channel option. */ public EpisodicProgramLoadTask(Context context, SeriesRecording seriesRecording) { this(context, Collections.singletonList(seriesRecording)); } @@ -94,64 +91,56 @@ abstract public class EpisodicProgramLoadTask { */ public EpisodicProgramLoadTask(Context context, Collection<SeriesRecording> seriesRecordings) { mContext = context.getApplicationContext(); - mDataManager = TvApplication.getSingletons(context).getDvrDataManager(); + mDataManager = TvSingletons.getSingletons(context).getDvrDataManager(); mSeriesRecordings.addAll(seriesRecordings); } - /** - * Returns the series recordings. - */ + /** Returns the series recordings. */ public List<SeriesRecording> getSeriesRecordings() { return mSeriesRecordings; } - /** - * Returns the program query task. It is {@code null} until it is executed. - */ + /** Returns the program query task. It is {@code null} until it is executed. */ @Nullable public AsyncProgramQueryTask getTask() { return mProgramQueryTask; } - /** - * Enables loading current programs. The default value is {@code false}. - */ + /** Enables loading current programs. The default value is {@code false}. */ public EpisodicProgramLoadTask setLoadCurrentProgram(boolean loadCurrentProgram) { - SoftPreconditions.checkState(mProgramQueryTask == null, TAG, - "Can't change setting after execution."); + SoftPreconditions.checkState( + mProgramQueryTask == null, TAG, "Can't change setting after execution."); mLoadCurrentProgram = loadCurrentProgram; return this; } - /** - * Enables already schedules episodes. The default value is {@code false}. - */ + /** Enables already schedules episodes. The default value is {@code false}. */ public EpisodicProgramLoadTask setLoadScheduledEpisode(boolean loadScheduledEpisode) { - SoftPreconditions.checkState(mProgramQueryTask == null, TAG, - "Can't change setting after execution."); + SoftPreconditions.checkState( + mProgramQueryTask == null, TAG, "Can't change setting after execution."); mLoadScheduledEpisode = loadScheduledEpisode; return this; } /** - * Enables loading disallowed programs whose schedules were removed manually by the user. - * The default value is {@code false}. + * Enables loading disallowed programs whose schedules were removed manually by the user. The + * default value is {@code false}. */ public EpisodicProgramLoadTask setLoadDisallowedProgram(boolean loadDisallowedProgram) { - SoftPreconditions.checkState(mProgramQueryTask == null, TAG, - "Can't change setting after execution."); + SoftPreconditions.checkState( + mProgramQueryTask == null, TAG, "Can't change setting after execution."); mLoadDisallowedProgram = loadDisallowedProgram; return this; } /** - * Gives the option whether to ignore the channel option when matching programs. - * If {@code ignoreChannelOption} is {@code true}, the program will be matched with - * {@link SeriesRecording#OPTION_CHANNEL_ALL} option. + * Gives the option whether to ignore the channel option when matching programs. If {@code + * ignoreChannelOption} is {@code true}, the program will be matched with {@link + * SeriesRecording#OPTION_CHANNEL_ALL} option. */ public EpisodicProgramLoadTask setIgnoreChannelOption(boolean ignoreChannelOption) { - SoftPreconditions.checkState(mProgramQueryTask == null, TAG, - "Can't change setting after execution."); + SoftPreconditions.checkState( + mProgramQueryTask == null, TAG, "Can't change setting after execution."); mIgnoreChannelOption = ignoreChannelOption; return this; } @@ -162,12 +151,15 @@ abstract public class EpisodicProgramLoadTask { * @see com.android.tv.util.AsyncDbTask#executeOnDbThread */ public void execute() { - if (SoftPreconditions.checkState(mProgramQueryTask == null, TAG, + if (SoftPreconditions.checkState( + mProgramQueryTask == null, + TAG, "Can't execute task: the task is already running.")) { - mQueryAllChannels = mSeriesRecordings.size() > 1 - || mSeriesRecordings.get(0).getChannelOption() - == SeriesRecording.OPTION_CHANNEL_ALL - || mIgnoreChannelOption; + mQueryAllChannels = + mSeriesRecordings.size() > 1 + || mSeriesRecordings.get(0).getChannelOption() + == SeriesRecording.OPTION_CHANNEL_ALL + || mIgnoreChannelOption; mProgramQueryTask = createTask(); mProgramQueryTask.executeOnDbThread(); } @@ -184,22 +176,22 @@ abstract public class EpisodicProgramLoadTask { } } - /** - * Runs on the UI thread after the program loading finishes successfully. - */ - protected void onPostExecute(List<Program> programs) { - } + /** Runs on the UI thread after the program loading finishes successfully. */ + protected void onPostExecute(List<Program> programs) {} - /** - * Runs on the UI thread after the program loading was canceled. - */ - protected void onCancelled(List<Program> programs) { - } + /** Runs on the UI thread after the program loading was canceled. */ + protected void onCancelled(List<Program> programs) {} private AsyncProgramQueryTask createTask() { SqlParams sqlParams = createSqlParams(); - return new AsyncProgramQueryTask(mContext.getContentResolver(), sqlParams.uri, - sqlParams.selection, sqlParams.selectionArgs, null, sqlParams.filter) { + return new AsyncProgramQueryTask( + TvSingletons.getSingletons(mContext).getDbExecutor(), + mContext.getContentResolver(), + sqlParams.uri, + sqlParams.selection, + sqlParams.selectionArgs, + null, + sqlParams.filter) { @Override protected void onPostExecute(List<Program> programs) { EpisodicProgramLoadTask.this.onPostExecute(programs); @@ -217,8 +209,11 @@ abstract public class EpisodicProgramLoadTask { if (PermissionUtils.hasAccessAllEpg(mContext)) { sqlParams.uri = Programs.CONTENT_URI; // Base - StringBuilder selection = new StringBuilder(mLoadCurrentProgram - ? PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM : PROGRAM_PREDICATE); + StringBuilder selection = + new StringBuilder( + mLoadCurrentProgram + ? PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM + : PROGRAM_PREDICATE); List<String> args = new ArrayList<>(); args.add(Long.toString(System.currentTimeMillis())); // Channel option @@ -237,15 +232,21 @@ abstract public class EpisodicProgramLoadTask { } else { // The query includes the current program. Will be filtered if needed. if (mQueryAllChannels) { - sqlParams.uri = Programs.CONTENT_URI.buildUpon() - .appendQueryParameter(PARAM_START_TIME, - String.valueOf(System.currentTimeMillis())) - .appendQueryParameter(PARAM_END_TIME, String.valueOf(Long.MAX_VALUE)) - .build(); + sqlParams.uri = + Programs.CONTENT_URI + .buildUpon() + .appendQueryParameter( + PARAM_START_TIME, + String.valueOf(System.currentTimeMillis())) + .appendQueryParameter( + PARAM_END_TIME, String.valueOf(Long.MAX_VALUE)) + .build(); } else { - sqlParams.uri = TvContract.buildProgramsUriForChannel( - mSeriesRecordings.get(0).getChannelId(), - System.currentTimeMillis(), Long.MAX_VALUE); + sqlParams.uri = + TvContract.buildProgramsUriForChannel( + mSeriesRecordings.get(0).getChannelId(), + System.currentTimeMillis(), + Long.MAX_VALUE); } sqlParams.selection = null; sqlParams.selectionArgs = null; @@ -292,16 +293,19 @@ abstract public class EpisodicProgramLoadTask { for (SeriesRecording seriesRecording : mSeriesRecordings) { boolean programMatches; if (mIgnoreChannelOption) { - programMatches = seriesRecording.matchProgram(program, - SeriesRecording.OPTION_CHANNEL_ALL); + programMatches = + seriesRecording.matchProgram( + program, SeriesRecording.OPTION_CHANNEL_ALL); } else { programMatches = seriesRecording.matchProgram(program); } if (programMatches) { return mLoadScheduledEpisode - || !mSeasonEpisodeNumbers.contains(new SeasonEpisodeNumber( - seriesRecording.getId(), program.getSeasonNumber(), - program.getEpisodeNumber())); + || !mSeasonEpisodeNumbers.contains( + new SeasonEpisodeNumber( + seriesRecording.getId(), + program.getSeasonNumber(), + program.getEpisodeNumber())); } } return false; @@ -316,7 +320,8 @@ abstract public class EpisodicProgramLoadTask { @Override public boolean filter(Cursor c) { return (mLoadCurrentProgram || c.getLong(START_TIME_INDEX) > System.currentTimeMillis()) - && c.getInt(RECORDING_PROHIBITED_INDEX) != 0 && super.filter(c); + && c.getInt(RECORDING_PROHIBITED_INDEX) != 0 + && super.filter(c); } } diff --git a/src/com/android/tv/dvr/recorder/ConflictChecker.java b/src/com/android/tv/dvr/recorder/ConflictChecker.java index 8aa90116..bfd315e9 100644 --- a/src/com/android/tv/dvr/recorder/ConflictChecker.java +++ b/src/com/android/tv/dvr/recorder/ConflictChecker.java @@ -27,21 +27,19 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.ArraySet; import android.util.Log; - -import com.android.tv.ApplicationSingletons; import com.android.tv.InputSessionManager; import com.android.tv.InputSessionManager.OnTvViewChannelChangeListener; import com.android.tv.MainActivity; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.WeakHandler; -import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.DvrUiHelper; - import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,8 +48,9 @@ import java.util.concurrent.TimeUnit; /** * Checking the runtime conflict of DVR recording. - * <p> - * This class runs only while the {@link MainActivity} is resumed and holds the upcoming conflicts. + * + * <p>This class runs only while the {@link MainActivity} is resumed and holds the upcoming + * conflicts. */ @TargetApi(Build.VERSION_CODES.N) @MainThread @@ -87,24 +86,40 @@ public class ConflictChecker { private final ScheduledRecordingListener mScheduledRecordingListener = new ScheduledRecordingListener() { - @Override - public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { - if (DEBUG) Log.d(TAG, "onScheduledRecordingAdded: " + scheduledRecordings); - mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); - } + @Override + public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { + if (DEBUG) { + Log.d( + TAG, + "onScheduledRecordingAdded: " + + Arrays.toString(scheduledRecordings)); + } + mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); + } - @Override - public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { - if (DEBUG) Log.d(TAG, "onScheduledRecordingRemoved: " + scheduledRecordings); - mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); - } + @Override + public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { + if (DEBUG) { + Log.d( + TAG, + "onScheduledRecordingRemoved: " + + Arrays.toString(scheduledRecordings)); + } + mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); + } - @Override - public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) { - if (DEBUG) Log.d(TAG, "onScheduledRecordingStatusChanged: " + scheduledRecordings); - mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); - } - }; + @Override + public void onScheduledRecordingStatusChanged( + ScheduledRecording... scheduledRecordings) { + if (DEBUG) { + Log.d( + TAG, + "onScheduledRecordingStatusChanged: " + + Arrays.toString(scheduledRecordings)); + } + mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); + } + }; private final OnTvViewChannelChangeListener mOnTvViewChannelChangeListener = new OnTvViewChannelChangeListener() { @@ -118,15 +133,13 @@ public class ConflictChecker { public ConflictChecker(MainActivity mainActivity) { mMainActivity = mainActivity; - ApplicationSingletons appSingletons = TvApplication.getSingletons(mainActivity); - mChannelDataManager = appSingletons.getChannelDataManager(); - mScheduleManager = appSingletons.getDvrScheduleManager(); - mSessionManager = appSingletons.getInputSessionManager(); + TvSingletons tvSingletons = TvSingletons.getSingletons(mainActivity); + mChannelDataManager = tvSingletons.getChannelDataManager(); + mScheduleManager = tvSingletons.getDvrScheduleManager(); + mSessionManager = tvSingletons.getInputSessionManager(); } - /** - * Starts checking the conflict. - */ + /** Starts checking the conflict. */ public void start() { if (mStarted) { return; @@ -137,9 +150,7 @@ public class ConflictChecker { mSessionManager.addOnTvViewChannelChangeListener(mOnTvViewChannelChangeListener); } - /** - * Stops checking the conflict. - */ + /** Stops checking the conflict. */ public void stop() { if (!mStarted) { return; @@ -150,23 +161,17 @@ public class ConflictChecker { mHandler.removeCallbacksAndMessages(null); } - /** - * Returns the upcoming conflicts. - */ + /** Returns the upcoming conflicts. */ public List<ScheduledRecording> getUpcomingConflicts() { return new ArrayList<>(mUpcomingConflicts); } - /** - * Adds a {@link OnUpcomingConflictChangeListener}. - */ + /** Adds a {@link OnUpcomingConflictChangeListener}. */ public void addOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) { mOnUpcomingConflictChangeListeners.add(listener); } - /** - * Removes the {@link OnUpcomingConflictChangeListener}. - */ + /** Removes the {@link OnUpcomingConflictChangeListener}. */ public void removeOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) { mOnUpcomingConflictChangeListeners.remove(listener); } @@ -177,9 +182,7 @@ public class ConflictChecker { } } - /** - * Remembers the user's decision to record while watching the channel. - */ + /** Remembers the user's decision to record while watching the channel. */ public void setCheckedConflictsForChannel(long mChannelId, List<ScheduledRecording> conflicts) { mCheckedConflictsMap.put(mChannelId, new ArrayList<>(conflicts)); } @@ -190,8 +193,7 @@ public class ConflictChecker { if (DEBUG) Log.d(TAG, "Handling MSG_CHECK_CONFLICT"); mHandler.removeMessages(MSG_CHECK_CONFLICT); mUpcomingConflicts.clear(); - if (!mScheduleManager.isInitialized() - || !mChannelDataManager.isDbLoadFinished()) { + if (!mScheduleManager.isInitialized() || !mChannelDataManager.isDbLoadFinished()) { mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, CHECK_RETRY_PERIOD_MS); notifyUpcomingConflictChanged(); return; @@ -209,8 +211,8 @@ public class ConflictChecker { long channelId = ContentUris.parseId(channelUri); Channel channel = mChannelDataManager.getChannel(channelId); // The conflicts caused by watching the channel. - List<ScheduledRecording> conflicts = mScheduleManager - .getConflictingSchedulesForWatching(channel.getId()); + List<ScheduledRecording> conflicts = + mScheduleManager.getConflictingSchedulesForWatching(channel.getId()); long earliestToCheck = Long.MAX_VALUE; long currentTimeMs = System.currentTimeMillis(); for (ScheduledRecording schedule : conflicts) { @@ -239,18 +241,15 @@ public class ConflictChecker { } } if (earliestToCheck != Long.MAX_VALUE) { - mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, - earliestToCheck - currentTimeMs); + mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, earliestToCheck - currentTimeMs); } if (DEBUG) Log.d(TAG, "upcoming conflicts: " + mUpcomingConflicts); notifyUpcomingConflictChanged(); if (!mUpcomingConflicts.isEmpty() && !DvrUiHelper.isChannelWatchConflictDialogShown(mMainActivity)) { // Don't show the conflict dialog if the user already knows. - List<ScheduledRecording> checkedConflicts = mCheckedConflictsMap.get( - channel.getId()); - if (checkedConflicts == null - || !checkedConflicts.containsAll(mUpcomingConflicts)) { + List<ScheduledRecording> checkedConflicts = mCheckedConflictsMap.get(channel.getId()); + if (checkedConflicts == null || !checkedConflicts.containsAll(mUpcomingConflicts)) { DvrUiHelper.showChannelWatchConflictDialog(mMainActivity, channel); } } @@ -271,9 +270,7 @@ public class ConflictChecker { } } - /** - * A listener for the change of upcoming conflicts. - */ + /** A listener for the change of upcoming conflicts. */ public interface OnUpcomingConflictChangeListener { void onUpcomingConflictChange(); } diff --git a/src/com/android/tv/dvr/recorder/DvrRecordingService.java b/src/com/android/tv/dvr/recorder/DvrRecordingService.java index 5d324ca5..9fdbf062 100644 --- a/src/com/android/tv/dvr/recorder/DvrRecordingService.java +++ b/src/com/android/tv/dvr/recorder/DvrRecordingService.java @@ -29,21 +29,20 @@ import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.annotation.VisibleForTesting; import android.util.Log; - -import com.android.tv.ApplicationSingletons; import com.android.tv.InputSessionManager; import com.android.tv.InputSessionManager.OnRecordingSessionChangeListener; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.common.feature.CommonFeatures; +import com.android.tv.common.util.Clock; import com.android.tv.dvr.WritableDvrDataManager; -import com.android.tv.util.Clock; import com.android.tv.util.RecurringRunner; /** - * DVR recording service. This service should be a foreground service and send a notification - * to users to do long-running recording task. + * DVR recording service. This service should be a foreground service and send a notification to + * users to do long-running recording task. * * <p>This service is waken up when there's a scheduled recording coming soon and at boot completed * since schedules have to be loaded from databases in order to set new recording alarms, which @@ -67,9 +66,9 @@ public class DvrRecordingService extends Service { /** * Starts the service in foreground. * - * @param startForRecording {@code true} if there are upcoming recordings in - * {@link RecordingScheduler#SOON_DURATION_IN_MS} and the service is - * started in foreground for those recordings. + * @param startForRecording {@code true} if there are upcoming recordings in {@link + * RecordingScheduler#SOON_DURATION_IN_MS} and the service is started in foreground for + * those recordings. */ @MainThread static void startForegroundService(Context context, boolean startForRecording) { @@ -99,7 +98,8 @@ public class DvrRecordingService extends Service { @VisibleForTesting boolean mIsRecording; private boolean mForeground; - @VisibleForTesting final OnRecordingSessionChangeListener mOnRecordingSessionChangeListener = + @VisibleForTesting + final OnRecordingSessionChangeListener mOnRecordingSessionChangeListener = new OnRecordingSessionChangeListener() { @Override public void onRecordingSessionChange(final boolean create, final int count) { @@ -114,18 +114,22 @@ public class DvrRecordingService extends Service { @Override public void onCreate() { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); if (DEBUG) Log.d(TAG, "onCreate"); super.onCreate(); SoftPreconditions.checkFeatureEnabled(this, CommonFeatures.DVR, TAG); sInstance = this; - ApplicationSingletons singletons = TvApplication.getSingletons(this); + TvSingletons singletons = TvSingletons.getSingletons(this); WritableDvrDataManager dataManager = (WritableDvrDataManager) singletons.getDvrDataManager(); mSessionManager = singletons.getInputSessionManager(); mSessionManager.addOnRecordingSessionChangeListener(mOnRecordingSessionChangeListener); - mReaperRunner = new RecurringRunner(this, java.util.concurrent.TimeUnit.DAYS.toMillis(1), - new ScheduledProgramReaper(dataManager, Clock.SYSTEM), null); + mReaperRunner = + new RecurringRunner( + this, + java.util.concurrent.TimeUnit.DAYS.toMillis(1), + new ScheduledProgramReaper(dataManager, Clock.SYSTEM), + null); mReaperRunner.start(); mContentTitle = getString(R.string.dvr_notification_content_title); mContentTextRecording = getString(R.string.dvr_notification_content_text_recording); @@ -179,13 +183,16 @@ public class DvrRecordingService extends Service { @VisibleForTesting protected void startForegroundInternal(boolean hasUpcomingRecording) { - // STOPSHIP: Replace the content title with real UX strings - Notification.Builder builder = new Notification.Builder(this) - .setContentTitle(mContentTitle) - .setContentText(hasUpcomingRecording ? mContentTextRecording : mContentTextLoading) - .setSmallIcon(R.drawable.ic_dvr); - Notification notification = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? - builder.setChannelId(DVR_NOTIFICATION_CHANNEL_ID).build() : builder.build(); + Notification.Builder builder = + new Notification.Builder(this) + .setContentTitle(mContentTitle) + .setContentText( + hasUpcomingRecording ? mContentTextRecording : mContentTextLoading) + .setSmallIcon(R.drawable.ic_dvr); + Notification notification = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? builder.setChannelId(DVR_NOTIFICATION_CHANNEL_ID).build() + : builder.build(); startForeground(ONGOING_NOTIFICATION_ID, notification); } @@ -196,10 +203,11 @@ public class DvrRecordingService extends Service { private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // STOPSHIP: Replace the channel name with real UX strings - mNotificationChannel = new NotificationChannel(DVR_NOTIFICATION_CHANNEL_ID, - getString(R.string.dvr_notification_channel_name), - NotificationManager.IMPORTANCE_LOW); + mNotificationChannel = + new NotificationChannel( + DVR_NOTIFICATION_CHANNEL_ID, + getString(R.string.dvr_notification_channel_name), + NotificationManager.IMPORTANCE_LOW); ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) .createNotificationChannel(mNotificationChannel); } diff --git a/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java b/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java index f1c0020b..bb5ea99d 100644 --- a/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java +++ b/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java @@ -21,18 +21,16 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.support.annotation.RequiresApi; +import com.android.tv.Starter; +import com.android.tv.TvSingletons; -import com.android.tv.TvApplication; - -/** - * Signals the DVR to start recording shows <i>soon</i>. - */ +/** Signals the DVR to start recording shows <i>soon</i>. */ @RequiresApi(Build.VERSION_CODES.N) public class DvrStartRecordingReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - TvApplication.setCurrentRunningProcess(context, true); - RecordingScheduler scheduler = TvApplication.getSingletons(context).getRecordingScheduler(); + Starter.start(context); + RecordingScheduler scheduler = TvSingletons.getSingletons(context).getRecordingScheduler(); if (scheduler != null) { scheduler.updateAndStartServiceIfNeeded(); } diff --git a/src/com/android/tv/dvr/recorder/InputTaskScheduler.java b/src/com/android/tv/dvr/recorder/InputTaskScheduler.java index fee4568e..1021b2bc 100644 --- a/src/com/android/tv/dvr/recorder/InputTaskScheduler.java +++ b/src/com/android/tv/dvr/recorder/InputTaskScheduler.java @@ -25,17 +25,15 @@ import android.support.annotation.VisibleForTesting; import android.util.ArrayMap; import android.util.Log; import android.util.LongSparseArray; - import com.android.tv.InputSessionManager; -import com.android.tv.data.Channel; +import com.android.tv.common.util.Clock; import com.android.tv.data.ChannelDataManager; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.WritableDvrDataManager; import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.util.Clock; import com.android.tv.util.CompositeComparator; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -43,9 +41,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -/** - * The scheduler for a TV input. - */ +/** The scheduler for a TV input. */ public class InputTaskScheduler { private static final String TAG = "InputTaskScheduler"; private static final boolean DEBUG = false; @@ -66,9 +62,7 @@ public class InputTaskScheduler { RecordingTask.END_TIME_COMPARATOR, RecordingTask.ID_COMPARATOR); - /** - * Returns the comparator which the schedules are sorted with when executed. - */ + /** Returns the comparator which the schedules are sorted with when executed. */ public static Comparator<ScheduledRecording> getRecordingOrderComparator() { return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR; } @@ -81,8 +75,8 @@ public class InputTaskScheduler { private final long mId; private final RecordingTask mTask; - HandlerWrapper(Looper looper, ScheduledRecording scheduledRecording, - RecordingTask recordingTask) { + HandlerWrapper( + Looper looper, ScheduledRecording scheduledRecording, RecordingTask recordingTask) { super(looper, recordingTask); mId = scheduledRecording.getId(); mTask = recordingTask; @@ -94,7 +88,7 @@ public class InputTaskScheduler { // The RecordingTask gets a chance first. // It must return false to pass this message to here. if (msg.what == MESSAGE_REMOVE) { - if (DEBUG) Log.d(TAG, "done " + mId); + if (DEBUG) Log.d(TAG, "done " + mId); mPendingRecordings.remove(mId); } removeCallbacksAndMessages(null); @@ -120,17 +114,37 @@ public class InputTaskScheduler { private final Object mInputLock = new Object(); private final RecordingTaskFactory mRecordingTaskFactory; - public InputTaskScheduler(Context context, TvInputInfo input, Looper looper, - ChannelDataManager channelDataManager, DvrManager dvrManager, - DvrDataManager dataManager, InputSessionManager sessionManager, Clock clock) { - this(context, input, looper, channelDataManager, dvrManager, dataManager, sessionManager, - clock, null); + public InputTaskScheduler( + Context context, + TvInputInfo input, + Looper looper, + ChannelDataManager channelDataManager, + DvrManager dvrManager, + DvrDataManager dataManager, + InputSessionManager sessionManager, + Clock clock) { + this( + context, + input, + looper, + channelDataManager, + dvrManager, + dataManager, + sessionManager, + clock, + null); } @VisibleForTesting - InputTaskScheduler(Context context, TvInputInfo input, Looper looper, - ChannelDataManager channelDataManager, DvrManager dvrManager, - DvrDataManager dataManager, InputSessionManager sessionManager, Clock clock, + InputTaskScheduler( + Context context, + TvInputInfo input, + Looper looper, + ChannelDataManager channelDataManager, + DvrManager dvrManager, + DvrDataManager dataManager, + InputSessionManager sessionManager, + Clock clock, RecordingTaskFactory recordingTaskFactory) { if (DEBUG) Log.d(TAG, "Creating scheduler for " + input); mContext = context; @@ -142,22 +156,32 @@ public class InputTaskScheduler { mSessionManager = sessionManager; mClock = clock; mMainThreadHandler = new Handler(Looper.getMainLooper()); - mRecordingTaskFactory = recordingTaskFactory != null ? recordingTaskFactory - : new RecordingTaskFactory() { - @Override - public RecordingTask createRecordingTask(ScheduledRecording schedule, Channel channel, - DvrManager dvrManager, InputSessionManager sessionManager, - WritableDvrDataManager dataManager, Clock clock) { - return new RecordingTask(mContext, schedule, channel, mDvrManager, mSessionManager, - mDataManager, mClock); - } - }; + mRecordingTaskFactory = + recordingTaskFactory != null + ? recordingTaskFactory + : new RecordingTaskFactory() { + @Override + public RecordingTask createRecordingTask( + ScheduledRecording schedule, + Channel channel, + DvrManager dvrManager, + InputSessionManager sessionManager, + WritableDvrDataManager dataManager, + Clock clock) { + return new RecordingTask( + mContext, + schedule, + channel, + mDvrManager, + mSessionManager, + mDataManager, + mClock); + } + }; mHandler = new WorkerThreadHandler(looper); } - /** - * Adds a {@link ScheduledRecording}. - */ + /** Adds a {@link ScheduledRecording}. */ public void addSchedule(ScheduledRecording schedule) { mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_SCHEDULED_RECORDING, schedule)); } @@ -173,9 +197,7 @@ public class InputTaskScheduler { mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE); } - /** - * Removes the {@link ScheduledRecording}. - */ + /** Removes the {@link ScheduledRecording}. */ public void removeSchedule(ScheduledRecording schedule) { mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_SCHEDULED_RECORDING, schedule)); } @@ -194,9 +216,7 @@ public class InputTaskScheduler { } } - /** - * Updates the {@link ScheduledRecording}. - */ + /** Updates the {@link ScheduledRecording}. */ public void updateSchedule(ScheduledRecording schedule) { mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_SCHEDULED_RECORDING, schedule)); } @@ -224,18 +244,14 @@ public class InputTaskScheduler { } } - /** - * Updates the TV input. - */ + /** Updates the TV input. */ public void updateTvInputInfo(TvInputInfo input) { synchronized (mInputLock) { mInput = input; } } - /** - * Stops the input task scheduler. - */ + /** Stops the input task scheduler. */ public void stop() { mHandler.removeCallbacksAndMessages(null); mHandler.sendEmptyMessage(MSG_STOP_SCHEDULE); @@ -262,7 +278,9 @@ public class InputTaskScheduler { ScheduledRecording schedule = iter.next(); if (schedule.getEndTimeMs() - currentTimeMs <= MIN_REMAIN_DURATION_PERCENT * schedule.getDuration()) { - fail(schedule); + Log.e(TAG, "Error! Program ended before recording started:" + schedule); + fail(schedule, + ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED); iter.remove(); } } @@ -274,7 +292,8 @@ public class InputTaskScheduler { for (ScheduledRecording schedule : mWaitingSchedules.values()) { if (schedule.getState() != ScheduledRecording.STATE_RECORDING_CANCELED && schedule.getStartTimeMs() - RecordingTask.RECORDING_EARLY_START_OFFSET_MS - <= currentTimeMs && schedule.getEndTimeMs() > currentTimeMs) { + <= currentTimeMs + && schedule.getEndTimeMs() > currentTimeMs) { schedulesToStart.add(schedule); } } @@ -321,10 +340,12 @@ public class InputTaskScheduler { earliest = schedule.getEndTimeMs(); } } else { - if (earliest > schedule.getStartTimeMs() - - RecordingTask.RECORDING_EARLY_START_OFFSET_MS) { - earliest = schedule.getStartTimeMs() - - RecordingTask.RECORDING_EARLY_START_OFFSET_MS; + if (earliest + > schedule.getStartTimeMs() + - RecordingTask.RECORDING_EARLY_START_OFFSET_MS) { + earliest = + schedule.getStartTimeMs() + - RecordingTask.RECORDING_EARLY_START_OFFSET_MS; } } } @@ -333,8 +354,9 @@ public class InputTaskScheduler { private RecordingTask createRecordingTask(ScheduledRecording schedule) { Channel channel = mChannelDataManager.getChannel(schedule.getChannelId()); - RecordingTask recordingTask = mRecordingTaskFactory.createRecordingTask(schedule, channel, - mDvrManager, mSessionManager, mDataManager, mClock); + RecordingTask recordingTask = + mRecordingTaskFactory.createRecordingTask( + schedule, channel, mDvrManager, mSessionManager, mDataManager, mClock); HandlerWrapper handlerWrapper = new HandlerWrapper(mLooper, schedule, recordingTask); mPendingRecordings.put(schedule.getId(), handlerWrapper); return recordingTask; @@ -369,21 +391,24 @@ public class InputTaskScheduler { return candidate; } - private void fail(ScheduledRecording schedule) { + private void fail(ScheduledRecording schedule, int reason) { // It's called when the scheduling has been failed without creating RecordingTask. - runOnMainHandler(new Runnable() { - @Override - public void run() { - ScheduledRecording scheduleInManager = - mDataManager.getScheduledRecording(schedule.getId()); - if (scheduleInManager != null) { - // The schedule should be updated based on the object from DataManager in case - // when it has been updated. - mDataManager.changeState(scheduleInManager, - ScheduledRecording.STATE_RECORDING_FAILED); - } - } - }); + runOnMainHandler( + new Runnable() { + @Override + public void run() { + ScheduledRecording scheduleInManager = + mDataManager.getScheduledRecording(schedule.getId()); + if (scheduleInManager != null) { + // The schedule should be updated based on the object from DataManager + // in case when it has been updated. + mDataManager.changeState( + scheduleInManager, + ScheduledRecording.STATE_RECORDING_FAILED, + reason); + } + } + }); } private void runOnMainHandler(Runnable runnable) { @@ -396,9 +421,13 @@ public class InputTaskScheduler { @VisibleForTesting interface RecordingTaskFactory { - RecordingTask createRecordingTask(ScheduledRecording scheduledRecording, Channel channel, - DvrManager dvrManager, InputSessionManager sessionManager, - WritableDvrDataManager dataManager, Clock clock); + RecordingTask createRecordingTask( + ScheduledRecording scheduledRecording, + Channel channel, + DvrManager dvrManager, + InputSessionManager sessionManager, + WritableDvrDataManager dataManager, + Clock clock); } private class WorkerThreadHandler extends Handler { @@ -417,6 +446,7 @@ public class InputTaskScheduler { break; case MSG_UPDATE_SCHEDULED_RECORDING: handleUpdateSchedule((ScheduledRecording) msg.obj); + break; case MSG_BUILD_SCHEDULE: handleBuildSchedule(); break; diff --git a/src/com/android/tv/dvr/recorder/RecordingScheduler.java b/src/com/android/tv/dvr/recorder/RecordingScheduler.java index cbaf46b5..f309537d 100644 --- a/src/com/android/tv/dvr/recorder/RecordingScheduler.java +++ b/src/com/android/tv/dvr/recorder/RecordingScheduler.java @@ -31,11 +31,10 @@ import android.support.annotation.VisibleForTesting; import android.util.ArrayMap; import android.util.Log; import android.util.Range; - -import com.android.tv.ApplicationSingletons; import com.android.tv.InputSessionManager; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; +import com.android.tv.common.util.Clock; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.ChannelDataManager.Listener; import com.android.tv.dvr.DvrDataManager; @@ -44,22 +43,21 @@ import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.WritableDvrDataManager; import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.util.Clock; import com.android.tv.util.TvInputManagerHelper; import com.android.tv.util.Utils; - import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; /** - * The core class to manage DVR schedule and run recording task. - ** - * <p> This class is responsible for: + * The core class to manage DVR schedule and run recording task. * + * + * <p>This class is responsible for: + * * <ul> - * <li>Sending record commands to TV inputs</li> - * <li>Resolving conflicting schedules, handling overlapping recording time durations, etc.</li> + * <li>Sending record commands to TV inputs + * <li>Resolving conflicting schedules, handling overlapping recording time durations, etc. * </ul> * * <p>This should be a singleton associated with application's main process. @@ -71,8 +69,8 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco private static final boolean DEBUG = false; private static final String HANDLER_THREAD_NAME = "RecordingScheduler"; - private final static long SOON_DURATION_IN_MS = TimeUnit.MINUTES.toMillis(1); - @VisibleForTesting final static long MS_TO_WAKE_BEFORE_START = TimeUnit.SECONDS.toMillis(30); + private static final long SOON_DURATION_IN_MS = TimeUnit.MINUTES.toMillis(1); + @VisibleForTesting static final long MS_TO_WAKE_BEFORE_START = TimeUnit.SECONDS.toMillis(30); private final Looper mLooper; private final InputSessionManager mSessionManager; @@ -98,21 +96,22 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco } }; - private Listener mChannelDataLoadListener = new Listener() { - @Override - public void onLoadFinished() { - mChannelDataManager.removeListener(this); - if (isDbLoaded()) { - updateInternal(); - } - } + private Listener mChannelDataLoadListener = + new Listener() { + @Override + public void onLoadFinished() { + mChannelDataManager.removeListener(this); + if (isDbLoaded()) { + updateInternal(); + } + } - @Override - public void onChannelListUpdated() { } + @Override + public void onChannelListUpdated() {} - @Override - public void onChannelBrowsableChanged() { } - }; + @Override + public void onChannelBrowsableChanged() {} + }; /** * Creates a scheduler to schedule alarms for scheduled recordings and create recording tasks. @@ -120,21 +119,32 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco */ public static RecordingScheduler createScheduler(Context context) { SoftPreconditions.checkState( - TvApplication.getSingletons(context).getRecordingScheduler() == null); + TvSingletons.getSingletons(context).getRecordingScheduler() == null); HandlerThread handlerThread = new HandlerThread(HANDLER_THREAD_NAME); handlerThread.start(); - ApplicationSingletons singletons = TvApplication.getSingletons(context); - return new RecordingScheduler(handlerThread.getLooper(), - singletons.getDvrManager(), singletons.getInputSessionManager(), + TvSingletons singletons = TvSingletons.getSingletons(context); + return new RecordingScheduler( + handlerThread.getLooper(), + singletons.getDvrManager(), + singletons.getInputSessionManager(), (WritableDvrDataManager) singletons.getDvrDataManager(), - singletons.getChannelDataManager(), singletons.getTvInputManagerHelper(), context, - Clock.SYSTEM, (AlarmManager) context.getSystemService(Context.ALARM_SERVICE)); + singletons.getChannelDataManager(), + singletons.getTvInputManagerHelper(), + context, + Clock.SYSTEM, + (AlarmManager) context.getSystemService(Context.ALARM_SERVICE)); } @VisibleForTesting - RecordingScheduler(Looper looper, DvrManager dvrManager, InputSessionManager sessionManager, - WritableDvrDataManager dataManager, ChannelDataManager channelDataManager, - TvInputManagerHelper inputManager, Context context, Clock clock, + RecordingScheduler( + Looper looper, + DvrManager dvrManager, + InputSessionManager sessionManager, + WritableDvrDataManager dataManager, + ChannelDataManager channelDataManager, + TvInputManagerHelper inputManager, + Context context, + Clock clock, AlarmManager alarmManager) { mLooper = looper; mDvrManager = dvrManager; @@ -159,9 +169,7 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco } } - /** - * Start recording that will happen soon, and set the next alarm time. - */ + /** Start recording that will happen soon, and set the next alarm time. */ public void updateAndStartServiceIfNeeded() { if (DEBUG) Log.d(TAG, "update and start service if needed"); if (isDbLoaded()) { @@ -185,8 +193,10 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco } private boolean updatePendingRecordings() { - List<ScheduledRecording> scheduledRecordings = mDataManager - .getScheduledRecordings(new Range<>(mLastStartTimePendingMs, + List<ScheduledRecording> scheduledRecordings = + mDataManager.getScheduledRecordings( + new Range<>( + mLastStartTimePendingMs, mClock.currentTimeMillis() + SOON_DURATION_IN_MS), ScheduledRecording.STATE_RECORDING_NOT_STARTED); for (ScheduledRecording r : scheduledRecordings) { @@ -198,7 +208,8 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco // recording service being wrongly pushed back to background in updateInternal(). return scheduledRecordings.size() > 0 || (mLastStartTimePendingMs > mClock.currentTimeMillis() - && mLastStartTimePendingMs < mClock.currentTimeMillis() + SOON_DURATION_IN_MS); + && mLastStartTimePendingMs + < mClock.currentTimeMillis() + SOON_DURATION_IN_MS); } private boolean isDbLoaded() { @@ -269,18 +280,32 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); if (input == null) { Log.e(TAG, "Can't find input for " + schedule); - mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED); + mDataManager.changeState( + schedule, + ScheduledRecording.STATE_RECORDING_FAILED, + ScheduledRecording.FAILED_REASON_INPUT_UNAVAILABLE); return; } if (!input.canRecord() || input.getTunerCount() <= 0) { Log.e(TAG, "TV input doesn't support recording: " + input); - mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED); + mDataManager.changeState( + schedule, + ScheduledRecording.STATE_RECORDING_FAILED, + ScheduledRecording.FAILED_REASON_INPUT_DVR_UNSUPPORTED); return; } InputTaskScheduler inputTaskScheduler = mInputSchedulerMap.get(input.getId()); if (inputTaskScheduler == null) { - inputTaskScheduler = new InputTaskScheduler(mContext, input, mLooper, - mChannelDataManager, mDvrManager, mDataManager, mSessionManager, mClock); + inputTaskScheduler = + new InputTaskScheduler( + mContext, + input, + mLooper, + mChannelDataManager, + mDvrManager, + mDataManager, + mSessionManager, + mClock); mInputSchedulerMap.put(input.getId(), inputTaskScheduler); } inputTaskScheduler.addSchedule(schedule); @@ -290,8 +315,9 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco } private void updateNextAlarm() { - long nextStartTime = mDataManager.getNextScheduledStartTimeAfter( - Math.max(mLastStartTimePendingMs, mClock.currentTimeMillis())); + long nextStartTime = + mDataManager.getNextScheduledStartTimeAfter( + Math.max(mLastStartTimePendingMs, mClock.currentTimeMillis())); if (nextStartTime != DvrDataManager.NEXT_START_TIME_NOT_FOUND) { long wakeAt = nextStartTime - MS_TO_WAKE_BEFORE_START; if (DEBUG) Log.d(TAG, "Set alarm to record at " + wakeAt); diff --git a/src/com/android/tv/dvr/recorder/RecordingTask.java b/src/com/android/tv/dvr/recorder/RecordingTask.java index 14888056..07a29e51 100644 --- a/src/com/android/tv/dvr/recorder/RecordingTask.java +++ b/src/com/android/tv/dvr/recorder/RecordingTask.java @@ -26,24 +26,24 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.util.Log; import android.widget.Toast; - import com.android.tv.InputSessionManager; import com.android.tv.InputSessionManager.RecordingSession; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.common.util.Clock; +import com.android.tv.common.util.CommonUtils; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.WritableDvrDataManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.recorder.InputTaskScheduler.HandlerWrapper; -import com.android.tv.util.Clock; import com.android.tv.util.Utils; - import java.util.Comparator; import java.util.concurrent.TimeUnit; @@ -55,58 +55,45 @@ import java.util.concurrent.TimeUnit; */ @WorkerThread @TargetApi(Build.VERSION_CODES.N) -public class RecordingTask extends RecordingCallback implements Handler.Callback, - DvrManager.Listener { +public class RecordingTask extends RecordingCallback + implements Handler.Callback, DvrManager.Listener { private static final String TAG = "RecordingTask"; private static final boolean DEBUG = false; - /** - * Compares the end time in ascending order. - */ - public static final Comparator<RecordingTask> END_TIME_COMPARATOR - = new Comparator<RecordingTask>() { - @Override - public int compare(RecordingTask lhs, RecordingTask rhs) { - return Long.compare(lhs.getEndTimeMs(), rhs.getEndTimeMs()); - } - }; - - /** - * Compares ID in ascending order. - */ - public static final Comparator<RecordingTask> ID_COMPARATOR - = new Comparator<RecordingTask>() { - @Override - public int compare(RecordingTask lhs, RecordingTask rhs) { - return Long.compare(lhs.getScheduleId(), rhs.getScheduleId()); - } - }; - - /** - * Compares the priority in ascending order. - */ - public static final Comparator<RecordingTask> PRIORITY_COMPARATOR - = new Comparator<RecordingTask>() { - @Override - public int compare(RecordingTask lhs, RecordingTask rhs) { - return Long.compare(lhs.getPriority(), rhs.getPriority()); - } - }; + /** Compares the end time in ascending order. */ + public static final Comparator<RecordingTask> END_TIME_COMPARATOR = + new Comparator<RecordingTask>() { + @Override + public int compare(RecordingTask lhs, RecordingTask rhs) { + return Long.compare(lhs.getEndTimeMs(), rhs.getEndTimeMs()); + } + }; + + /** Compares ID in ascending order. */ + public static final Comparator<RecordingTask> ID_COMPARATOR = + new Comparator<RecordingTask>() { + @Override + public int compare(RecordingTask lhs, RecordingTask rhs) { + return Long.compare(lhs.getScheduleId(), rhs.getScheduleId()); + } + }; + + /** Compares the priority in ascending order. */ + public static final Comparator<RecordingTask> PRIORITY_COMPARATOR = + new Comparator<RecordingTask>() { + @Override + public int compare(RecordingTask lhs, RecordingTask rhs) { + return Long.compare(lhs.getPriority(), rhs.getPriority()); + } + }; - @VisibleForTesting - static final int MSG_INITIALIZE = 1; - @VisibleForTesting - static final int MSG_START_RECORDING = 2; - @VisibleForTesting - static final int MSG_STOP_RECORDING = 3; - /** - * Message to update schedule. - */ + @VisibleForTesting static final int MSG_INITIALIZE = 1; + @VisibleForTesting static final int MSG_START_RECORDING = 2; + @VisibleForTesting static final int MSG_STOP_RECORDING = 3; + /** Message to update schedule. */ public static final int MSG_UDPATE_SCHEDULE = 4; - /** - * The time when the start command will be sent before the recording starts. - */ + /** The time when the start command will be sent before the recording starts. */ public static final long RECORDING_EARLY_START_OFFSET_MS = TimeUnit.SECONDS.toMillis(3); /** * If the recording starts later than the scheduled start time or ends before the scheduled end @@ -126,6 +113,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback ERROR, RELEASED, } + private final InputSessionManager mSessionManager; private final DvrManager mDvrManager; private final Context mContext; @@ -142,9 +130,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback private Uri mRecordedProgramUri; private boolean mCanceled; - RecordingTask(Context context, ScheduledRecording scheduledRecording, Channel channel, - DvrManager dvrManager, InputSessionManager sessionManager, - WritableDvrDataManager dataManager, Clock clock) { + RecordingTask( + Context context, + ScheduledRecording scheduledRecording, + Channel channel, + DvrManager dvrManager, + InputSessionManager sessionManager, + WritableDvrDataManager dataManager, + Clock clock) { mContext = context; mScheduledRecording = scheduledRecording; mChannel = channel; @@ -163,8 +156,10 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback @Override public boolean handleMessage(Message msg) { if (DEBUG) Log.d(TAG, "handleMessage " + msg); - SoftPreconditions.checkState(msg.what == HandlerWrapper.MESSAGE_REMOVE || mHandler != null, - TAG, "Null handler trying to handle " + msg); + SoftPreconditions.checkState( + msg.what == HandlerWrapper.MESSAGE_REMOVE || mHandler != null, + TAG, + "Null handler trying to handle " + msg); try { switch (msg.what) { case MSG_INITIALIZE: @@ -185,7 +180,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback release(); return false; default: - SoftPreconditions.checkArgument(false, TAG, "unexpected message type " + msg); + SoftPreconditions.checkArgument(false, TAG, "unexpected message type %s", msg); break; } return true; @@ -200,7 +195,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback public void onDisconnected(String inputId) { if (DEBUG) Log.d(TAG, "onDisconnected(" + inputId + ")"); if (mRecordingSession != null && mState != State.FINISHED) { - failAndQuit(); + failAndQuit(ScheduledRecording.FAILED_REASON_NOT_FINISHED); } } @@ -208,7 +203,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback public void onConnectionFailed(String inputId) { if (DEBUG) Log.d(TAG, "onConnectionFailed(" + inputId + ")"); if (mRecordingSession != null) { - failAndQuit(); + failAndQuit(ScheduledRecording.FAILED_REASON_CONNECTION_FAILED); } } @@ -219,23 +214,27 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback return; } mState = State.CONNECTED; - if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MSG_START_RECORDING, - mScheduledRecording.getStartTimeMs() - RECORDING_EARLY_START_OFFSET_MS)) { - failAndQuit(); + if (mHandler == null + || !sendEmptyMessageAtAbsoluteTime( + MSG_START_RECORDING, + mScheduledRecording.getStartTimeMs() - RECORDING_EARLY_START_OFFSET_MS)) { + failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT); } } @Override public void onRecordingStopped(Uri recordedProgramUri) { - if (DEBUG) Log.d(TAG, "onRecordingStopped"); + Log.i(TAG, "Recording Stopped: " + mScheduledRecording); + Log.i(TAG, "Recording Stopped: stored as " + recordedProgramUri); if (mRecordingSession == null) { return; } mRecordedProgramUri = recordedProgramUri; mState = State.FINISHED; int state = ScheduledRecording.STATE_RECORDING_FINISHED; - if (mStartedWithClipping || mScheduledRecording.getEndTimeMs() - CLIPPED_THRESHOLD_MS - > mClock.currentTimeMillis()) { + if (mStartedWithClipping + || mScheduledRecording.getEndTimeMs() - CLIPPED_THRESHOLD_MS + > mClock.currentTimeMillis()) { state = ScheduledRecording.STATE_RECORDING_CLIPPED; } updateRecordingState(state); @@ -247,65 +246,89 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback @Override public void onError(int reason) { - if (DEBUG) Log.d(TAG, "onError reason " + reason); + Log.i(TAG, "Recording failed with code=" + reason + " for " + mScheduledRecording); if (mRecordingSession == null) { return; } + int error; switch (reason) { case TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE: - mMainThreadHandler.post(new Runnable() { - @Override - public void run() { - if (TvApplication.getSingletons(mContext).getMainActivityWrapper() - .isResumed()) { - ScheduledRecording scheduledRecording = mDataManager - .getScheduledRecording(mScheduledRecording.getId()); - if (scheduledRecording != null) { - Toast.makeText(mContext.getApplicationContext(), - mContext.getString(R.string - .dvr_error_insufficient_space_description_one_recording, - scheduledRecording.getProgramDisplayTitle(mContext)), - Toast.LENGTH_LONG) - .show(); + Log.i(TAG, "Insufficient space to record " + mScheduledRecording); + mMainThreadHandler.post( + new Runnable() { + @Override + public void run() { + if (TvSingletons.getSingletons(mContext) + .getMainActivityWrapper() + .isResumed()) { + ScheduledRecording scheduledRecording = + mDataManager.getScheduledRecording( + mScheduledRecording.getId()); + if (scheduledRecording != null) { + Toast.makeText( + mContext.getApplicationContext(), + mContext.getString( + R.string + .dvr_error_insufficient_space_description_one_recording, + scheduledRecording + .getProgramDisplayTitle( + mContext)), + Toast.LENGTH_LONG) + .show(); + } + } else { + Utils.setRecordingFailedReason( + mContext.getApplicationContext(), + TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); + Utils.addFailedScheduledRecordingInfo( + mContext.getApplicationContext(), + mScheduledRecording.getProgramDisplayTitle(mContext)); + } } - } else { - Utils.setRecordingFailedReason(mContext.getApplicationContext(), - TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); - Utils.addFailedScheduledRecordingInfo(mContext.getApplicationContext(), - mScheduledRecording.getProgramDisplayTitle(mContext)); - } - } - }); - // Pass through + }); + error = ScheduledRecording.FAILED_REASON_INSUFFICIENT_SPACE; + break; + case TvInputManager.RECORDING_ERROR_RESOURCE_BUSY: + error = ScheduledRecording.FAILED_REASON_RESOURCE_BUSY; + break; default: - failAndQuit(); + error = ScheduledRecording.FAILED_REASON_OTHER; break; } + failAndQuit(error); } private void handleInit() { if (DEBUG) Log.d(TAG, "handleInit " + mScheduledRecording); if (mScheduledRecording.getEndTimeMs() < mClock.currentTimeMillis()) { Log.w(TAG, "End time already past, not recording " + mScheduledRecording); - failAndQuit(); + failAndQuit(ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED); return; } if (mChannel == null) { Log.w(TAG, "Null channel for " + mScheduledRecording); - failAndQuit(); + failAndQuit(ScheduledRecording.FAILED_REASON_INVALID_CHANNEL); return; } if (mChannel.getId() != mScheduledRecording.getChannelId()) { - Log.w(TAG, "Channel" + mChannel + " does not match scheduled recording " - + mScheduledRecording); - failAndQuit(); + Log.w( + TAG, + "Channel" + + mChannel + + " does not match scheduled recording " + + mScheduledRecording); + failAndQuit(ScheduledRecording.FAILED_REASON_INVALID_CHANNEL); return; } String inputId = mChannel.getInputId(); - mRecordingSession = mSessionManager.createRecordingSession(inputId, - "recordingTask-" + mScheduledRecording.getId(), this, - mHandler, mScheduledRecording.getEndTimeMs()); + mRecordingSession = + mSessionManager.createRecordingSession( + inputId, + "recordingTask-" + mScheduledRecording.getId(), + this, + mHandler, + mScheduledRecording.getEndTimeMs()); mState = State.SESSION_ACQUIRED; mDvrManager.addListener(this, mHandler); mRecordingSession.tune(inputId, mChannel.getUri()); @@ -313,8 +336,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback } private void failAndQuit() { + failAndQuit(ScheduledRecording.FAILED_REASON_OTHER); + } + + private void failAndQuit(Integer reason) { if (DEBUG) Log.d(TAG, "failAndQuit"); - updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED); + updateRecordingState( + ScheduledRecording.STATE_RECORDING_FAILED, + reason); mState = State.ERROR; sendRemove(); } @@ -322,16 +351,18 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback private void sendRemove() { if (DEBUG) Log.d(TAG, "sendRemove"); if (mHandler != null) { - mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage( - HandlerWrapper.MESSAGE_REMOVE)); + mHandler.sendMessageAtFrontOfQueue( + mHandler.obtainMessage(HandlerWrapper.MESSAGE_REMOVE)); } } private void handleStartRecording() { - if (DEBUG) Log.d(TAG, "handleStartRecording " + mScheduledRecording); + Log.i(TAG, "Start Recording: " + mScheduledRecording); long programId = mScheduledRecording.getProgramId(); - mRecordingSession.startRecording(programId == ScheduledRecording.ID_NOT_SET ? null - : TvContract.buildProgramUri(programId)); + mRecordingSession.startRecording( + programId == ScheduledRecording.ID_NOT_SET + ? null + : TvContract.buildProgramUri(programId)); updateRecordingState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS); // If it starts late, it's clipped. if (mScheduledRecording.getStartTimeMs() + CLIPPED_THRESHOLD_MS @@ -340,14 +371,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback } mState = State.RECORDING_STARTED; - if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING, - mScheduledRecording.getEndTimeMs())) { - failAndQuit(); + if (!sendEmptyMessageAtAbsoluteTime( + MSG_STOP_RECORDING, mScheduledRecording.getEndTimeMs())) { + failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT); } } private void handleStopRecording() { - if (DEBUG) Log.d(TAG, "handleStopRecording " + mScheduledRecording); + Log.i(TAG, "Stop Recording: " + mScheduledRecording); mRecordingSession.stopRecording(); mState = State.RECORDING_STOP_REQUESTED; } @@ -362,7 +393,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback if (mState == State.RECORDING_STARTED) { mHandler.removeMessages(MSG_STOP_RECORDING); if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING, schedule.getEndTimeMs())) { - failAndQuit(); + failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT); } } } @@ -377,23 +408,17 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback return mScheduledRecording.getId(); } - /** - * Returns the priority. - */ + /** Returns the priority. */ public long getPriority() { return mScheduledRecording.getPriority(); } - /** - * Returns the start time of the recording. - */ + /** Returns the start time of the recording. */ public long getStartTimeMs() { return mScheduledRecording.getStartTimeMs(); } - /** - * Returns the end time of the recording. - */ + /** Returns the end time of the recording. */ public long getEndTimeMs() { return mScheduledRecording.getEndTimeMs(); } @@ -410,33 +435,53 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback long now = mClock.currentTimeMillis(); long delay = Math.max(0L, when - now); if (DEBUG) { - Log.d(TAG, "Sending message " + what + " with a delay of " + delay / 1000 - + " seconds to arrive at " + Utils.toIsoDateTimeString(when)); + Log.d( + TAG, + "Sending message " + + what + + " with a delay of " + + delay / 1000 + + " seconds to arrive at " + + CommonUtils.toIsoDateTimeString(when)); } return mHandler.sendEmptyMessageDelayed(what, delay); } private void updateRecordingState(@ScheduledRecording.RecordingState int state) { - if (DEBUG) Log.d(TAG, "Updating the state of " + mScheduledRecording + " to " + state); - mScheduledRecording = ScheduledRecording.buildFrom(mScheduledRecording).setState(state) - .build(); - runOnMainThread(new Runnable() { - @Override - public void run() { - ScheduledRecording schedule = mDataManager.getScheduledRecording( - mScheduledRecording.getId()); - if (schedule == null) { - // Schedule has been deleted. Delete the recorded program. - removeRecordedProgram(); - } else { - // Update the state based on the object in DataManager in case when it has been - // updated. mScheduledRecording will be updated from - // onScheduledRecordingStateChanged. - mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule) - .setState(state).build()); - } - } - }); + updateRecordingState(state, null); + } + private void updateRecordingState( + @ScheduledRecording.RecordingState int state, @Nullable Integer reason) { + if (DEBUG) { + Log.d(TAG, "Updating the state of " + mScheduledRecording + " to " + state); + } + mScheduledRecording = + ScheduledRecording.buildFrom(mScheduledRecording).setState(state).build(); + runOnMainThread( + new Runnable() { + @Override + public void run() { + ScheduledRecording schedule = + mDataManager.getScheduledRecording(mScheduledRecording.getId()); + if (schedule == null) { + // Schedule has been deleted. Delete the recorded program. + removeRecordedProgram(); + } else { + // Update the state based on the object in DataManager in case when it + // has been updated. mScheduledRecording will be updated from + // onScheduledRecordingStateChanged. + ScheduledRecording.Builder builder = + ScheduledRecording + .buildFrom(schedule) + .setState(state); + if (state == ScheduledRecording.STATE_RECORDING_FAILED + && reason != null) { + builder.setFailedReason(reason); + } + mDataManager.updateScheduledRecording(builder.build()); + } + } + }); } @Override @@ -447,16 +492,12 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback stop(); } - /** - * Starts the task. - */ + /** Starts the task. */ public void start() { mHandler.sendEmptyMessage(MSG_INITIALIZE); } - /** - * Stops the task. - */ + /** Stops the task. */ public void stop() { if (DEBUG) Log.d(TAG, "stop"); switch (mState) { @@ -480,9 +521,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback } } - /** - * Cancels the task - */ + /** Cancels the task */ public void cancel() { if (DEBUG) Log.d(TAG, "cancel"); mCanceled = true; @@ -490,12 +529,12 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback removeRecordedProgram(); } - /** - * Clean up the task. - */ + /** Clean up the task. */ public void cleanUp() { if (mState == State.RECORDING_STARTED || mState == State.RECORDING_STOP_REQUESTED) { - updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED); + updateRecordingState( + ScheduledRecording.STATE_RECORDING_FAILED, + ScheduledRecording.FAILED_REASON_SCHEDULER_STOPPED); } release(); if (mHandler != null) { @@ -509,14 +548,15 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback } private void removeRecordedProgram() { - runOnMainThread(new Runnable() { - @Override - public void run() { - if (mRecordedProgramUri != null) { - mDvrManager.removeRecordedProgram(mRecordedProgramUri); - } - } - }); + runOnMainThread( + new Runnable() { + @Override + public void run() { + if (mRecordedProgramUri != null) { + mDvrManager.removeRecordedProgram(mRecordedProgramUri); + } + } + }); } private void runOnMainThread(Runnable runnable) { diff --git a/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java b/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java index d958c4a1..dd106e1c 100644 --- a/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java +++ b/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java @@ -17,24 +17,18 @@ package com.android.tv.dvr.recorder; import android.support.annotation.MainThread; -import android.support.annotation.VisibleForTesting; - +import com.android.tv.common.util.Clock; import com.android.tv.dvr.WritableDvrDataManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.util.Clock; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -/** - * Deletes {@link ScheduledRecording} older than {@value @DAYS} days. - */ -class ScheduledProgramReaper implements Runnable { +/** Deletes {@link ScheduledRecording} older than {@value @DAYS} days. */ +public class ScheduledProgramReaper implements Runnable { - @VisibleForTesting - static final int DAYS = 2; + public static final int DAYS = 7; private final WritableDvrDataManager mDvrDataManager; private final Clock mClock; @@ -54,7 +48,7 @@ class ScheduledProgramReaper implements Runnable { // series recording. if (r.getEndTimeMs() < cutoff && (r.getSeriesRecordingId() == SeriesRecording.ID_NOT_SET - || r.getState() != ScheduledRecording.STATE_RECORDING_FINISHED)) { + || r.getState() != ScheduledRecording.STATE_RECORDING_FINISHED)) { toRemove.add(r); } } diff --git a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java index 15508c24..4f7a789b 100644 --- a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java +++ b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java @@ -27,27 +27,23 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.LongSparseArray; - -import com.android.tv.ApplicationSingletons; -import com.android.tv.TvApplication; -import com.android.tv.common.CollectionUtils; -import com.android.tv.common.SharedPreferencesUtils; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; +import com.android.tv.common.experiments.Experiments; +import com.android.tv.common.util.CollectionUtils; +import com.android.tv.common.util.SharedPreferencesUtils; import com.android.tv.data.Program; -import com.android.tv.data.epg.EpgFetcher; +import com.android.tv.data.epg.EpgReader; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.WritableDvrDataManager; -import com.android.tv.dvr.data.SeasonEpisodeNumber; import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.data.SeasonEpisodeNumber; import com.android.tv.dvr.data.SeriesInfo; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.provider.EpisodicProgramLoadTask; -import com.android.tv.experiments.Experiments; - -import com.android.tv.util.LocationUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -60,12 +56,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import javax.inject.Provider; /** - * Creates the {@link com.android.tv.dvr.data.ScheduledRecording}s for - * the {@link com.android.tv.dvr.data.SeriesRecording}. - * <p> - * The current implementation assumes that the series recordings are scheduled only for one channel. + * Creates the {@link com.android.tv.dvr.data.ScheduledRecording}s for the {@link + * com.android.tv.dvr.data.SeriesRecording}. + * + * <p>The current implementation assumes that the series recordings are scheduled only for one + * channel. */ @TargetApi(Build.VERSION_CODES.N) public class SeriesRecordingScheduler { @@ -78,9 +76,7 @@ public class SeriesRecordingScheduler { @SuppressLint("StaticFieldLeak") private static SeriesRecordingScheduler sInstance; - /** - * Creates and returns the {@link SeriesRecordingScheduler}. - */ + /** Creates and returns the {@link SeriesRecordingScheduler}. */ public static synchronized SeriesRecordingScheduler getInstance(Context context) { if (sInstance == null) { sInstance = new SeriesRecordingScheduler(context); @@ -100,54 +96,59 @@ public class SeriesRecordingScheduler { private boolean mPaused; private final Set<Long> mPendingSeriesRecordings = new ArraySet<>(); - private final SeriesRecordingListener mSeriesRecordingListener = new SeriesRecordingListener() { - @Override - public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { - for (SeriesRecording seriesRecording : seriesRecordings) { - executeFetchSeriesInfoTask(seriesRecording); - } - } - - @Override - public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { - // Cancel the update. - for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator(); - iter.hasNext(); ) { - SeriesRecordingUpdateTask task = iter.next(); - if (CollectionUtils.subtract(task.getSeriesRecordings(), seriesRecordings, - SeriesRecording.ID_COMPARATOR).isEmpty()) { - task.cancel(true); - iter.remove(); + private final SeriesRecordingListener mSeriesRecordingListener = + new SeriesRecordingListener() { + @Override + public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { + for (SeriesRecording seriesRecording : seriesRecordings) { + executeFetchSeriesInfoTask(seriesRecording); + } } - } - for (SeriesRecording seriesRecording : seriesRecordings) { - FetchSeriesInfoTask task = mFetchSeriesInfoTasks.get(seriesRecording.getId()); - if (task != null) { - task.cancel(true); - mFetchSeriesInfoTasks.remove(seriesRecording.getId()); + + @Override + public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { + // Cancel the update. + for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator(); + iter.hasNext(); ) { + SeriesRecordingUpdateTask task = iter.next(); + if (CollectionUtils.subtract( + task.getSeriesRecordings(), + seriesRecordings, + SeriesRecording.ID_COMPARATOR) + .isEmpty()) { + task.cancel(true); + iter.remove(); + } + } + for (SeriesRecording seriesRecording : seriesRecordings) { + FetchSeriesInfoTask task = + mFetchSeriesInfoTasks.get(seriesRecording.getId()); + if (task != null) { + task.cancel(true); + mFetchSeriesInfoTasks.remove(seriesRecording.getId()); + } + } } - } - } - @Override - public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { - List<SeriesRecording> stopped = new ArrayList<>(); - List<SeriesRecording> normal = new ArrayList<>(); - for (SeriesRecording r : seriesRecordings) { - if (r.isStopped()) { - stopped.add(r); - } else { - normal.add(r); + @Override + public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { + List<SeriesRecording> stopped = new ArrayList<>(); + List<SeriesRecording> normal = new ArrayList<>(); + for (SeriesRecording r : seriesRecordings) { + if (r.isStopped()) { + stopped.add(r); + } else { + normal.add(r); + } + } + if (!stopped.isEmpty()) { + onSeriesRecordingRemoved(SeriesRecording.toArray(stopped)); + } + if (!normal.isEmpty()) { + updateSchedules(normal); + } } - } - if (!stopped.isEmpty()) { - onSeriesRecordingRemoved(SeriesRecording.toArray(stopped)); - } - if (!normal.isEmpty()) { - updateSchedules(normal); - } - } - }; + }; private final ScheduledRecordingListener mScheduledRecordingListener = new ScheduledRecordingListener() { @@ -166,7 +167,8 @@ public class SeriesRecordingScheduler { List<ScheduledRecording> schedulesForUpdate = new ArrayList<>(); for (ScheduledRecording r : schedules) { if ((r.getState() == ScheduledRecording.STATE_RECORDING_FAILED - || r.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED) + || r.getState() + == ScheduledRecording.STATE_RECORDING_CLIPPED) && r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET && !TextUtils.isEmpty(r.getSeasonNumber()) && !TextUtils.isEmpty(r.getEpisodeNumber())) { @@ -205,18 +207,17 @@ public class SeriesRecordingScheduler { private SeriesRecordingScheduler(Context context) { mContext = context.getApplicationContext(); - ApplicationSingletons appSingletons = TvApplication.getSingletons(context); - mDvrManager = appSingletons.getDvrManager(); - mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager(); - mSharedPreferences = context.getSharedPreferences( - SharedPreferencesUtils.SHARED_PREF_SERIES_RECORDINGS, Context.MODE_PRIVATE); - mFetchedSeriesIds.addAll(mSharedPreferences.getStringSet(KEY_FETCHED_SERIES_IDS, - Collections.emptySet())); + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + mDvrManager = tvSingletons.getDvrManager(); + mDataManager = (WritableDvrDataManager) tvSingletons.getDvrDataManager(); + mSharedPreferences = + context.getSharedPreferences( + SharedPreferencesUtils.SHARED_PREF_SERIES_RECORDINGS, Context.MODE_PRIVATE); + mFetchedSeriesIds.addAll( + mSharedPreferences.getStringSet(KEY_FETCHED_SERIES_IDS, Collections.emptySet())); } - /** - * Starts the scheduler. - */ + /** Starts the scheduler. */ @MainThread public void start() { SoftPreconditions.checkState(mDataManager.isInitialized()); @@ -261,15 +262,16 @@ public class SeriesRecordingScheduler { private void executeFetchSeriesInfoTask(SeriesRecording seriesRecording) { if (Experiments.CLOUD_EPG.get()) { - FetchSeriesInfoTask task = new FetchSeriesInfoTask(seriesRecording); + FetchSeriesInfoTask task = + new FetchSeriesInfoTask( + seriesRecording, + TvSingletons.getSingletons(mContext).providesEpgReader()); task.execute(); mFetchSeriesInfoTasks.put(seriesRecording.getId(), task); } } - /** - * Pauses the updates of the series recordings. - */ + /** Pauses the updates of the series recordings. */ public void pauseUpdate() { if (DEBUG) Log.d(TAG, "Schedule paused"); if (mPaused) { @@ -287,9 +289,7 @@ public class SeriesRecordingScheduler { } } - /** - * Resumes the updates of the series recordings. - */ + /** Resumes the updates of the series recordings. */ public void resumeUpdate() { if (DEBUG) Log.d(TAG, "Schedule resumed"); if (!mPaused) { @@ -329,25 +329,28 @@ public class SeriesRecordingScheduler { mPendingSeriesRecordings.add(r.getId()); } if (DEBUG) { - Log.d(TAG, "The scheduler has been paused. Adding to the pending list. size=" - + mPendingSeriesRecordings.size()); + Log.d( + TAG, + "The scheduler has been paused. Adding to the pending list. size=" + + mPendingSeriesRecordings.size()); } return; } Set<SeriesRecording> previousSeriesRecordings = new HashSet<>(); for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator(); - iter.hasNext(); ) { + iter.hasNext(); ) { SeriesRecordingUpdateTask task = iter.next(); - if (CollectionUtils.containsAny(task.getSeriesRecordings(), seriesRecordings, - SeriesRecording.ID_COMPARATOR)) { + if (CollectionUtils.containsAny( + task.getSeriesRecordings(), seriesRecordings, SeriesRecording.ID_COMPARATOR)) { // The task is affected by the seriesRecordings task.cancel(true); previousSeriesRecordings.addAll(task.getSeriesRecordings()); iter.remove(); } } - List<SeriesRecording> seriesRecordingsToUpdate = CollectionUtils.union(seriesRecordings, - previousSeriesRecordings, SeriesRecording.ID_COMPARATOR); + List<SeriesRecording> seriesRecordingsToUpdate = + CollectionUtils.union( + seriesRecordings, previousSeriesRecordings, SeriesRecording.ID_COMPARATOR); for (Iterator<SeriesRecording> iter = seriesRecordingsToUpdate.iterator(); iter.hasNext(); ) { SeriesRecording seriesRecording = mDataManager.getSeriesRecording(iter.next().getId()); @@ -367,8 +370,8 @@ public class SeriesRecordingScheduler { task.execute(); } else { for (SeriesRecording seriesRecording : seriesRecordingsToUpdate) { - SeriesRecordingUpdateTask task = new SeriesRecordingUpdateTask( - Collections.singletonList(seriesRecording)); + SeriesRecordingUpdateTask task = + new SeriesRecordingUpdateTask(Collections.singletonList(seriesRecording)); mScheduleTasks.add(task); if (DEBUG) Log.d(TAG, "Added schedule task: " + task); task.execute(); @@ -389,8 +392,9 @@ public class SeriesRecordingScheduler { * Pick one program per an episode. * * <p>Note that the programs which has been already scheduled have the highest priority, and all - * of them are added even though they are the same episodes. That's because the schedules - * should be added to the series recording. + * of them are added even though they are the same episodes. That's because the schedules should + * be added to the series recording. + * * <p>If there are no existing schedules for an episode, one program which starts earlier is * picked. */ @@ -399,11 +403,10 @@ public class SeriesRecordingScheduler { return pickOneProgramPerEpisode(mDataManager, seriesRecordings, programs); } - /** - * @see #pickOneProgramPerEpisode(List, List) - */ + /** @see #pickOneProgramPerEpisode(List, List) */ public static LongSparseArray<List<Program>> pickOneProgramPerEpisode( - DvrDataManager dataManager, List<SeriesRecording> seriesRecordings, + DvrDataManager dataManager, + List<SeriesRecording> seriesRecordings, List<Program> programs) { // Initialize. LongSparseArray<List<Program>> result = new LongSparseArray<>(); @@ -422,8 +425,11 @@ public class SeriesRecordingScheduler { result.get(seriesRecordingId).add(program); continue; } - SeasonEpisodeNumber seasonEpisodeNumber = new SeasonEpisodeNumber(seriesRecordingId, - program.getSeasonNumber(), program.getEpisodeNumber()); + SeasonEpisodeNumber seasonEpisodeNumber = + new SeasonEpisodeNumber( + seriesRecordingId, + program.getSeasonNumber(), + program.getEpisodeNumber()); List<Program> programsForEpisode = programsForEpisodeMap.get(seasonEpisodeNumber); if (programsForEpisode == null) { programsForEpisode = new ArrayList<>(); @@ -434,22 +440,24 @@ public class SeriesRecordingScheduler { // Pick one program. for (Entry<SeasonEpisodeNumber, List<Program>> entry : programsForEpisodeMap.entrySet()) { List<Program> programsForEpisode = entry.getValue(); - Collections.sort(programsForEpisode, new Comparator<Program>() { - @Override - public int compare(Program lhs, Program rhs) { - // Place the existing schedule first. - boolean lhsScheduled = isProgramScheduled(dataManager, lhs); - boolean rhsScheduled = isProgramScheduled(dataManager, rhs); - if (lhsScheduled && !rhsScheduled) { - return -1; - } - if (!lhsScheduled && rhsScheduled) { - return 1; - } - // Sort by the start time in ascending order. - return lhs.compareTo(rhs); - } - }); + Collections.sort( + programsForEpisode, + new Comparator<Program>() { + @Override + public int compare(Program lhs, Program rhs) { + // Place the existing schedule first. + boolean lhsScheduled = isProgramScheduled(dataManager, lhs); + boolean rhsScheduled = isProgramScheduled(dataManager, rhs); + if (lhsScheduled && !rhsScheduled) { + return -1; + } + if (!lhsScheduled && rhsScheduled) { + return 1; + } + // Sort by the start time in ascending order. + return lhs.compareTo(rhs); + } + }); boolean added = false; // Add all the scheduled programs List<Program> programsForSeries = result.get(entry.getKey().seriesRecordingId); @@ -469,8 +477,8 @@ public class SeriesRecordingScheduler { private static boolean isProgramScheduled(DvrDataManager dataManager, Program program) { ScheduledRecording schedule = dataManager.getScheduledRecordingForProgramId(program.getId()); - return schedule != null && schedule.getState() - == ScheduledRecording.STATE_RECORDING_NOT_STARTED; + return schedule != null + && schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED; } private void updateFetchedSeries() { @@ -478,8 +486,8 @@ public class SeriesRecordingScheduler { } /** - * This works only for the existing series recordings. Do not use this task for the - * "adding series recording" UI. + * This works only for the existing series recordings. Do not use this task for the "adding + * series recording" UI. */ private class SeriesRecordingUpdateTask extends EpisodicProgramLoadTask { SeriesRecordingUpdateTask(List<SeriesRecording> seriesRecordings) { @@ -491,16 +499,17 @@ public class SeriesRecordingScheduler { if (DEBUG) Log.d(TAG, "onPostExecute: updating schedules with programs:" + programs); mScheduleTasks.remove(this); if (programs == null) { - Log.e(TAG, "Creating schedules for series recording failed: " - + getSeriesRecordings()); + Log.e( + TAG, + "Creating schedules for series recording failed: " + getSeriesRecordings()); return; } - LongSparseArray<List<Program>> seriesProgramMap = pickOneProgramPerEpisode( - getSeriesRecordings(), programs); + LongSparseArray<List<Program>> seriesProgramMap = + pickOneProgramPerEpisode(getSeriesRecordings(), programs); for (SeriesRecording seriesRecording : getSeriesRecordings()) { // Check the series recording is still valid. - SeriesRecording actualSeriesRecording = mDataManager.getSeriesRecording( - seriesRecording.getId()); + SeriesRecording actualSeriesRecording = + mDataManager.getSeriesRecording(seriesRecording.getId()); if (actualSeriesRecording == null || actualSeriesRecording.isStopped()) { continue; } @@ -520,35 +529,39 @@ public class SeriesRecordingScheduler { @Override public String toString() { return "SeriesRecordingUpdateTask:{" - + "series_recordings=" + getSeriesRecordings() + + "series_recordings=" + + getSeriesRecordings() + "}"; } } private class FetchSeriesInfoTask extends AsyncTask<Void, Void, SeriesInfo> { - private SeriesRecording mSeriesRecording; + private final SeriesRecording mSeriesRecording; + private final Provider<EpgReader> mEpgReaderProvider; - FetchSeriesInfoTask(SeriesRecording seriesRecording) { + FetchSeriesInfoTask( + SeriesRecording seriesRecording, Provider<EpgReader> epgReaderProvider) { mSeriesRecording = seriesRecording; + mEpgReaderProvider = epgReaderProvider; } @Override protected SeriesInfo doInBackground(Void... voids) { - return EpgFetcher.createEpgReader(mContext, LocationUtils.getCurrentCountry(mContext)) - .getSeriesInfo(mSeriesRecording.getSeriesId()); + return mEpgReaderProvider.get().getSeriesInfo(mSeriesRecording.getSeriesId()); } @Override protected void onPostExecute(SeriesInfo seriesInfo) { if (seriesInfo != null) { - mDataManager.updateSeriesRecording(SeriesRecording.buildFrom(mSeriesRecording) - .setTitle(seriesInfo.getTitle()) - .setDescription(seriesInfo.getDescription()) - .setLongDescription(seriesInfo.getLongDescription()) - .setCanonicalGenreIds(seriesInfo.getCanonicalGenreIds()) - .setPosterUri(seriesInfo.getPosterUri()) - .setPhotoUri(seriesInfo.getPhotoUri()) - .build()); + mDataManager.updateSeriesRecording( + SeriesRecording.buildFrom(mSeriesRecording) + .setTitle(seriesInfo.getTitle()) + .setDescription(seriesInfo.getDescription()) + .setLongDescription(seriesInfo.getLongDescription()) + .setCanonicalGenreIds(seriesInfo.getCanonicalGenreIds()) + .setPosterUri(seriesInfo.getPosterUri()) + .setPhotoUri(seriesInfo.getPhotoUri()) + .build()); mFetchedSeriesIds.add(seriesInfo.getId()); updateFetchedSeries(); } diff --git a/src/com/android/tv/dvr/ui/BigArguments.java b/src/com/android/tv/dvr/ui/BigArguments.java index ec3b5065..0d6ff8b1 100644 --- a/src/com/android/tv/dvr/ui/BigArguments.java +++ b/src/com/android/tv/dvr/ui/BigArguments.java @@ -17,37 +17,27 @@ package com.android.tv.dvr.ui; import android.support.annotation.NonNull; - import com.android.tv.common.SoftPreconditions; - import java.util.HashMap; import java.util.Map; -/** - * Stores the object to pass through activities/fragments. - */ +/** Stores the object to pass through activities/fragments. */ public class BigArguments { - private final static String TAG = "BigArguments"; + private static final String TAG = "BigArguments"; private static Map<String, Object> sBigArgumentMap = new HashMap<>(); - /** - * Sets the argument. - */ + /** Sets the argument. */ public static void setArgument(String name, @NonNull Object value) { SoftPreconditions.checkState(value != null, TAG, "Set argument, but value is null"); sBigArgumentMap.put(name, value); } - /** - * Returns the argument which is associated to the name. - */ + /** Returns the argument which is associated to the name. */ public static Object getArgument(String name) { return sBigArgumentMap.get(name); } - /** - * Resets the arguments. - */ + /** Resets the arguments. */ public static void reset() { sBigArgumentMap.clear(); } diff --git a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java index cddece73..32679421 100644 --- a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java +++ b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java @@ -26,16 +26,14 @@ import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; import android.widget.ImageView.ScaleType; - import com.android.tv.R; - import java.util.Map; /** - * TODO: Remove this class once b/32405620 is fixed. - * This class is for the workaround of b/32405620 and only for the shared element transition between - * {@link com.android.tv.dvr.ui.browse.RecordingCardView} and - * {@link com.android.tv.dvr.ui.browse.DvrDetailsActivity}. + * TODO: Remove this class once b/32405620 is fixed. This class is for the workaround of b/32405620 + * and only for the shared element transition between {@link + * com.android.tv.dvr.ui.browse.RecordingCardView} and {@link + * com.android.tv.dvr.ui.browse.DvrDetailsActivity}. */ public class ChangeImageTransformWithScaledParent extends ChangeImageTransform { private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix"; @@ -60,7 +58,8 @@ public class ChangeImageTransformWithScaledParent extends ChangeImageTransform { View view = transitionValues.view; Map<String, Object> values = transitionValues.values; Matrix matrix = (Matrix) values.get(PROPNAME_MATRIX); - if (matrix != null && view.getId() == R.id.details_overview_image + if (matrix != null + && view.getId() == R.id.details_overview_image && view instanceof ImageView) { ImageView imageView = (ImageView) view; if (imageView.getScaleType() == ScaleType.CENTER_INSIDE @@ -68,10 +67,13 @@ public class ChangeImageTransformWithScaledParent extends ChangeImageTransform { Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); if (bitmap.getWidth() < imageView.getWidth() && bitmap.getHeight() < imageView.getHeight()) { - float scale = imageView.getContext().getResources().getFraction( - R.fraction.lb_focus_zoom_factor_medium, 1, 1); - matrix.postScale(scale, scale, imageView.getWidth() / 2, - imageView.getHeight() / 2); + float scale = + imageView + .getContext() + .getResources() + .getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1); + matrix.postScale( + scale, scale, imageView.getWidth() / 2, imageView.getHeight() / 2); } } } diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java index 62327870..fce94230 100644 --- a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java +++ b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java @@ -24,13 +24,11 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.RecordedProgram; - import java.util.List; /** @@ -51,13 +49,19 @@ public class DvrAlreadyRecordedFragment extends DvrGuidedStepFragment { public void onAttach(Context context) { super.onAttach(context); mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); - DvrManager dvrManager = TvApplication.getSingletons(context).getDvrManager(); - mDuplicate = dvrManager.getRecordedProgram(mProgram.getTitle(), - mProgram.getSeasonNumber(), mProgram.getEpisodeNumber()); + DvrManager dvrManager = TvSingletons.getSingletons(context).getDvrManager(); + mDuplicate = + dvrManager.getRecordedProgram( + mProgram.getTitle(), + mProgram.getSeasonNumber(), + mProgram.getEpisodeNumber()); if (mDuplicate == null) { dvrManager.addSchedule(mProgram); - DvrUiHelper.showAddScheduleToast(context, mProgram.getTitle(), - mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis()); + DvrUiHelper.showAddScheduleToast( + context, + mProgram.getTitle(), + mProgram.getStartTimeUtcMillis(), + mProgram.getEndTimeUtcMillis()); dismissDialog(); } } @@ -74,18 +78,21 @@ public class DvrAlreadyRecordedFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { Context context = getContext(); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_RECORD_ANYWAY) - .title(R.string.dvr_action_record_anyway) - .build()); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_WATCH) - .title(R.string.dvr_action_watch_now) - .build()); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_CANCEL) - .title(R.string.dvr_action_record_cancel) - .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_RECORD_ANYWAY) + .title(R.string.dvr_action_record_anyway) + .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_WATCH) + .title(R.string.dvr_action_watch_now) + .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_CANCEL) + .title(R.string.dvr_action_record_cancel) + .build()); } @Override diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java index 6da75e55..456ad830 100644 --- a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java +++ b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java @@ -25,13 +25,11 @@ import android.support.annotation.NonNull; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.text.format.DateUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.ScheduledRecording; - import java.util.List; /** @@ -52,13 +50,19 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment { public void onAttach(Context context) { super.onAttach(context); mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); - DvrManager dvrManager = TvApplication.getSingletons(context).getDvrManager(); - mDuplicate = dvrManager.getScheduledRecording(mProgram.getTitle(), - mProgram.getSeasonNumber(), mProgram.getEpisodeNumber()); + DvrManager dvrManager = TvSingletons.getSingletons(context).getDvrManager(); + mDuplicate = + dvrManager.getScheduledRecording( + mProgram.getTitle(), + mProgram.getSeasonNumber(), + mProgram.getEpisodeNumber()); if (mDuplicate == null) { dvrManager.addSchedule(mProgram); - DvrUiHelper.showAddScheduleToast(context, mProgram.getTitle(), - mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis()); + DvrUiHelper.showAddScheduleToast( + context, + mProgram.getTitle(), + mProgram.getStartTimeUtcMillis(), + mProgram.getEndTimeUtcMillis()); dismissDialog(); } } @@ -67,9 +71,13 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment { @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { String title = getString(R.string.dvr_already_scheduled_dialog_title); - String description = getString(R.string.dvr_already_scheduled_dialog_description, - DateUtils.formatDateTime(getContext(), mDuplicate.getStartTimeMs(), - DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE)); + String description = + getString( + R.string.dvr_already_scheduled_dialog_description, + DateUtils.formatDateTime( + getContext(), + mDuplicate.getStartTimeMs(), + DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE)); Drawable image = getResources().getDrawable(R.drawable.ic_warning_white_96dp, null); return new Guidance(title, description, null, image); } @@ -77,18 +85,21 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { Context context = getContext(); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_RECORD_ANYWAY) - .title(R.string.dvr_action_record_anyway) - .build()); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_RECORD_INSTEAD) - .title(R.string.dvr_action_record_instead) - .build()); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_CANCEL) - .title(R.string.dvr_action_record_cancel) - .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_RECORD_ANYWAY) + .title(R.string.dvr_action_record_anyway) + .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_RECORD_INSTEAD) + .title(R.string.dvr_action_record_instead) + .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_CANCEL) + .title(R.string.dvr_action_record_cancel) + .build()); } @Override diff --git a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java index 36659412..6be35cb2 100644 --- a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java +++ b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java @@ -21,15 +21,13 @@ import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelRecordConflictFragment; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -43,8 +41,10 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen Bundle args = getArguments(); if (args != null) { long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID); - mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager() - .getChannel(channelId); + mChannel = + TvSingletons.getSingletons(getContext()) + .getChannelDataManager() + .getChannel(channelId); } SoftPreconditions.checkArgument(mChannel != null); super.onCreate(savedInstanceState); @@ -66,32 +66,36 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen mDurations.add(TimeUnit.HOURS.toMillis(1)); mDurations.add(TimeUnit.HOURS.toMillis(3)); - actions.add(new GuidedAction.Builder(getContext()) - .id(++actionId) - .title(R.string.recording_start_dialog_10_min_duration) - .build()); - actions.add(new GuidedAction.Builder(getContext()) - .id(++actionId) - .title(R.string.recording_start_dialog_30_min_duration) - .build()); - actions.add(new GuidedAction.Builder(getContext()) - .id(++actionId) - .title(R.string.recording_start_dialog_1_hour_duration) - .build()); - actions.add(new GuidedAction.Builder(getContext()) - .id(++actionId) - .title(R.string.recording_start_dialog_3_hours_duration) - .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(++actionId) + .title(R.string.recording_start_dialog_10_min_duration) + .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(++actionId) + .title(R.string.recording_start_dialog_30_min_duration) + .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(++actionId) + .title(R.string.recording_start_dialog_1_hour_duration) + .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(++actionId) + .title(R.string.recording_start_dialog_3_hours_duration) + .build()); } @Override public void onTrackedGuidedActionClicked(GuidedAction action) { - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); long duration = mDurations.get((int) action.getId()); long startTimeMs = System.currentTimeMillis(); long endTimeMs = System.currentTimeMillis() + duration; - List<ScheduledRecording> conflicts = dvrManager.getConflictingSchedules( - mChannel.getId(), startTimeMs, endTimeMs); + List<ScheduledRecording> conflicts = + dvrManager.getConflictingSchedules(mChannel.getId(), startTimeMs, endTimeMs); dvrManager.addSchedule(mChannel, startTimeMs, endTimeMs); if (conflicts.isEmpty()) { dismissDialog(); @@ -102,8 +106,7 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen args.putLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS, startTimeMs); args.putLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS, endTimeMs); fragment.setArguments(args); - GuidedStepFragment.add(getFragmentManager(), fragment, - R.id.halfsized_dialog_host); + GuidedStepFragment.add(getFragmentManager(), fragment, R.id.halfsized_dialog_host); } } diff --git a/src/com/android/tv/dvr/ui/DvrConflictFragment.java b/src/com/android/tv/dvr/ui/DvrConflictFragment.java index 6f362e68..65759555 100644 --- a/src/com/android/tv/dvr/ui/DvrConflictFragment.java +++ b/src/com/android/tv/dvr/ui/DvrConflictFragment.java @@ -27,18 +27,16 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.android.tv.MainActivity; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; +import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.recorder.ConflictChecker; import com.android.tv.dvr.recorder.ConflictChecker.OnUpcomingConflictChangeListener; -import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -72,15 +70,16 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { } @Override - public void onCreateActions(@NonNull List<GuidedAction> actions, - Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getContext()) - .clickAction(GuidedAction.ACTION_ID_OK) - .build()); - actions.add(new GuidedAction.Builder(getContext()) - .id(ACTION_VIEW_SCHEDULES) - .title(R.string.dvr_action_view_schedules) - .build()); + public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { + actions.add( + new GuidedAction.Builder(getContext()) + .clickAction(GuidedAction.ACTION_ID_OK) + .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(ACTION_VIEW_SCHEDULES) + .title(R.string.dvr_action_view_schedules) + .build()); } @Override @@ -114,33 +113,45 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { } switch (titles.size()) { case 0: - Log.i(TAG, "Conflict has been resolved by any reason. Maybe input might have" - + " been deleted."); + Log.i( + TAG, + "Conflict has been resolved by any reason. Maybe input might have" + + " been deleted."); return null; case 1: - return getResources().getString( - R.string.dvr_program_conflict_dialog_description_1, titles.get(0)); + return getResources() + .getString( + R.string.dvr_program_conflict_dialog_description_1, titles.get(0)); case 2: - return getResources().getString( - R.string.dvr_program_conflict_dialog_description_2, titles.get(0), - titles.get(1)); + return getResources() + .getString( + R.string.dvr_program_conflict_dialog_description_2, + titles.get(0), + titles.get(1)); case 3: - return getResources().getString( - R.string.dvr_program_conflict_dialog_description_3, titles.get(0), - titles.get(1)); + return getResources() + .getString( + R.string.dvr_program_conflict_dialog_description_3, + titles.get(0), + titles.get(1)); default: - return getResources().getQuantityString( - R.plurals.dvr_program_conflict_dialog_description_many, - titles.size() - LISTED_PROGRAM_COUNT, titles.get(0), titles.get(1), - titles.size() - LISTED_PROGRAM_COUNT); + return getResources() + .getQuantityString( + R.plurals.dvr_program_conflict_dialog_description_many, + titles.size() - LISTED_PROGRAM_COUNT, + titles.get(0), + titles.get(1), + titles.size() - LISTED_PROGRAM_COUNT); } } @Nullable private String getScheduleTitle(ScheduledRecording schedule) { if (schedule.getType() == ScheduledRecording.TYPE_TIMED) { - Channel channel = TvApplication.getSingletons(getContext()).getChannelDataManager() - .getChannel(schedule.getChannelId()); + Channel channel = + TvSingletons.getSingletons(getContext()) + .getChannelDataManager() + .getChannel(schedule.getChannelId()); if (channel != null) { return channel.getDisplayName(); } else { @@ -151,14 +162,13 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { } } - /** - * A fragment to show the program conflict. - */ + /** A fragment to show the program conflict. */ public static class DvrProgramConflictFragment extends DvrConflictFragment { private Program mProgram; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); @@ -168,8 +178,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { SoftPreconditions.checkNotNull(input); List<ScheduledRecording> conflicts = null; if (input != null) { - conflicts = TvApplication.getSingletons(getContext()).getDvrManager() - .getConflictingSchedules(mProgram); + conflicts = + TvSingletons.getSingletons(getContext()) + .getDvrManager() + .getConflictingSchedules(mProgram); } if (conflicts == null) { conflicts = Collections.emptyList(); @@ -185,8 +197,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { String title = getResources().getString(R.string.dvr_program_conflict_dialog_title); - String descriptionPrefix = getString( - R.string.dvr_program_conflict_dialog_description_prefix, mProgram.getTitle()); + String descriptionPrefix = + getString( + R.string.dvr_program_conflict_dialog_description_prefix, + mProgram.getTitle()); String description = getConflictDescription(); if (description == null) { dismissDialog(); @@ -201,21 +215,21 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { } } - /** - * A fragment to show the channel recording conflict. - */ + /** A fragment to show the channel recording conflict. */ public static class DvrChannelRecordConflictFragment extends DvrConflictFragment { private Channel mChannel; private long mStartTimeMs; private long mEndTimeMs; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID); - mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager() - .getChannel(channelId); + mChannel = + TvSingletons.getSingletons(getContext()) + .getChannelDataManager() + .getChannel(channelId); SoftPreconditions.checkArgument(mChannel != null); TvInputInfo input = Utils.getTvInputInfoForChannelId(getContext(), mChannel.getId()); SoftPreconditions.checkNotNull(input); @@ -223,8 +237,11 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { if (input != null) { mStartTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS); mEndTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS); - conflicts = TvApplication.getSingletons(getContext()).getDvrManager() - .getConflictingSchedules(mChannel.getId(), mStartTimeMs, mEndTimeMs); + conflicts = + TvSingletons.getSingletons(getContext()) + .getDvrManager() + .getConflictingSchedules( + mChannel.getId(), mStartTimeMs, mEndTimeMs); } if (conflicts == null) { conflicts = Collections.emptyList(); @@ -240,9 +257,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { String title = getResources().getString(R.string.dvr_channel_conflict_dialog_title); - String descriptionPrefix = getString( - R.string.dvr_channel_conflict_dialog_description_prefix, - mChannel.getDisplayName()); + String descriptionPrefix = + getString( + R.string.dvr_channel_conflict_dialog_description_prefix, + mChannel.getDisplayName()); String description = getConflictDescription(); if (description == null) { dismissDialog(); @@ -259,16 +277,16 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { /** * A fragment to show the channel watching conflict. - * <p> - * This fragment is automatically closed when there are no upcoming conflicts. + * + * <p>This fragment is automatically closed when there are no upcoming conflicts. */ public static class DvrChannelWatchConflictFragment extends DvrConflictFragment implements OnUpcomingConflictChangeListener { private long mChannelId; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mChannelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID); @@ -298,24 +316,27 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment { @NonNull @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { - String title = getResources().getString( - R.string.dvr_epg_channel_watch_conflict_dialog_title); - String description = getResources().getString( - R.string.dvr_epg_channel_watch_conflict_dialog_description); + String title = + getResources().getString(R.string.dvr_epg_channel_watch_conflict_dialog_title); + String description = + getResources() + .getString(R.string.dvr_epg_channel_watch_conflict_dialog_description); return new Guidance(title, description, null, null); } @Override - public void onCreateActions(@NonNull List<GuidedAction> actions, - Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getContext()) - .id(ACTION_DELETE_CONFLICT) - .title(R.string.dvr_action_delete_schedule) - .build()); - actions.add(new GuidedAction.Builder(getContext()) - .id(ACTION_CANCEL) - .title(R.string.dvr_action_record_program) - .build()); + public void onCreateActions( + @NonNull List<GuidedAction> actions, Bundle savedInstanceState) { + actions.add( + new GuidedAction.Builder(getContext()) + .id(ACTION_DELETE_CONFLICT) + .title(R.string.dvr_action_delete_schedule) + .build()); + actions.add( + new GuidedAction.Builder(getContext()) + .id(ACTION_CANCEL) + .title(R.string.dvr_action_record_program) + .build()); } @Override diff --git a/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java b/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java new file mode 100644 index 00000000..677a6cbb --- /dev/null +++ b/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v17.leanback.widget.GuidanceStylist; +import android.support.v17.leanback.widget.GuidedAction; +import com.android.tv.TvSingletons; +import com.android.tv.data.Program; +import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.util.Utils; +import java.util.List; + +/** + * A fragment which shows the formation of a program. + */ +public class DvrFutureProgramInfoFragment extends DvrGuidedStepFragment { + private static final long ACTION_ID_VIEW_SCHEDULE = 1; + private ScheduledRecording mScheduledRecording; + private Program mProgram; + + @Override + public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { + long startTime = mProgram.getStartTimeUtcMillis(); + // TODO(b/71717923): use R.string when the strings are finalized + StringBuilder description = new StringBuilder() + .append("This program will start at ") + .append(Utils.getDurationString(getContext(), startTime, startTime, false)); + if (mScheduledRecording != null) { + description.append("\nThis program has been scheduled for recording."); + } + return new GuidanceStylist.Guidance( + mProgram.getTitle(), description.toString(), null, null); + } + + @Override + public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { + Activity activity = getActivity(); + mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); + mScheduledRecording = + TvSingletons.getSingletons(getContext()) + .getDvrDataManager() + .getScheduledRecordingForProgramId(mProgram.getId()); + actions.add( + new GuidedAction.Builder(activity) + .id(GuidedAction.ACTION_ID_OK) + .title(android.R.string.ok) + .build()); + if (mScheduledRecording != null) { + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_ID_VIEW_SCHEDULE) + .title("View schedules") + .build()); + } + + } + + @Override + public void onTrackedGuidedActionClicked(GuidedAction action) { + if (action.getId() == ACTION_ID_VIEW_SCHEDULE) { + DvrUiHelper.startSchedulesActivity(getContext(), mScheduledRecording); + return; + } + dismissDialog(); + } + + @Override + public String getTrackerPrefix() { + return "DvrFutureProgramInfoFragment"; + } +} diff --git a/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java b/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java index 6b0c22ff..611962d0 100644 --- a/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java +++ b/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java @@ -24,12 +24,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; - import com.android.tv.R; -/** - * Stylist class used for DVR settings {@link GuidedStepFragment}. - */ +/** Stylist class used for DVR settings {@link GuidedStepFragment}. */ public class DvrGuidedActionsStylist extends GuidedActionsStylist { private static boolean sInitialized; private static float sWidthWeight; @@ -68,11 +65,13 @@ public class DvrGuidedActionsStylist extends GuidedActionsStylist { return; } sInitialized = true; - sItemHeight = context.getResources().getDimensionPixelSize( - R.dimen.dvr_settings_one_line_action_container_height); + sItemHeight = + context.getResources() + .getDimensionPixelSize( + R.dimen.dvr_settings_one_line_action_container_height); TypedValue outValue = new TypedValue(); - context.getResources().getValue(R.dimen.dvr_settings_button_actions_list_width_weight, - outValue, true); + context.getResources() + .getValue(R.dimen.dvr_settings_button_actions_list_width_weight, outValue, true); sWidthWeight = outValue.getFloat(); } } diff --git a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java index ab852e10..a900cc70 100644 --- a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java +++ b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java @@ -26,31 +26,23 @@ import android.support.v17.leanback.widget.VerticalGridView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import com.android.tv.ApplicationSingletons; import com.android.tv.MainActivity; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; +import com.android.tv.common.recording.RecordingStorageStatusManager; import com.android.tv.dialog.HalfSizedDialogFragment.OnActionClickListener; import com.android.tv.dialog.SafeDismissDialogFragment; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.DvrStorageStatusManager; - import java.util.List; public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment { - /** - * Action ID for "recording/scheduling the program anyway". - */ + /** Action ID for "recording/scheduling the program anyway". */ public static final int ACTION_RECORD_ANYWAY = 1; - /** - * Action ID for "deleting existed recordings". - */ + /** Action ID for "deleting existed recordings". */ public static final int ACTION_DELETE_RECORDINGS = 2; - /** - * Action ID for "cancelling current recording request". - */ + /** Action ID for "cancelling current recording request". */ public static final int ACTION_CANCEL_RECORDING = 3; + public static final String UNKNOWN_DVR_ACTION = "Unknown DVR Action"; private DvrManager mDvrManager; @@ -63,13 +55,13 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment { @Override public void onAttach(Context context) { super.onAttach(context); - ApplicationSingletons singletons = TvApplication.getSingletons(context); + TvSingletons singletons = TvSingletons.getSingletons(context); mDvrManager = singletons.getDvrManager(); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); VerticalGridView actionsList = getGuidedActionsStylist().getActionsGridView(); actionsList.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE); @@ -122,32 +114,37 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment { } /** - * The inner guided step fragment for - * {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment + * The inner guided step fragment for {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment * .DvrNoFreeSpaceErrorDialogFragment}. */ public static class DvrNoFreeSpaceErrorFragment extends DvrGuidedStepFragment { @Override public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { - return new GuidanceStylist.Guidance(getString(R.string.dvr_error_no_free_space_title), - getString(R.string.dvr_error_no_free_space_description), null, null); + return new GuidanceStylist.Guidance( + getString(R.string.dvr_error_no_free_space_title), + getString(R.string.dvr_error_no_free_space_description), + null, + null); } @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_RECORD_ANYWAY) - .title(R.string.dvr_action_record_anyway) - .build()); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_DELETE_RECORDINGS) - .title(R.string.dvr_action_delete_recordings) - .build()); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_CANCEL_RECORDING) - .title(R.string.dvr_action_record_cancel) - .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_RECORD_ANYWAY) + .title(R.string.dvr_action_record_anyway) + .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_DELETE_RECORDINGS) + .title(R.string.dvr_action_delete_recordings) + .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_CANCEL_RECORDING) + .title(R.string.dvr_action_record_cancel) + .build()); } @Override @@ -157,29 +154,32 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment { } /** - * The inner guided step fragment for - * {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment + * The inner guided step fragment for {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment * .DvrSmallSizedStorageErrorDialogFragment}. */ public static class DvrSmallSizedStorageErrorFragment extends DvrGuidedStepFragment { @Override public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { - String title = getResources().getString( - R.string.dvr_error_small_sized_storage_title); - String description = getResources().getString( - R.string.dvr_error_small_sized_storage_description, - DvrStorageStatusManager.MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES / 1024 - / 1024 / 1024); + String title = getResources().getString(R.string.dvr_error_small_sized_storage_title); + String description = + getResources() + .getString( + R.string.dvr_error_small_sized_storage_description, + RecordingStorageStatusManager.MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES + / 1024 + / 1024 + / 1024); return new GuidanceStylist.Guidance(title, description, null, null); } @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .id(GuidedAction.ACTION_ID_OK) - .title(android.R.string.ok) - .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(GuidedAction.ACTION_ID_OK) + .title(android.R.string.ok) + .build()); } @Override @@ -192,4 +192,4 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment { return "DvrSmallSizedStorageErrorFragment"; } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java index f8ef3850..4a713703 100644 --- a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java +++ b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java @@ -20,47 +20,26 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; -import android.support.v17.leanback.widget.GuidedAction; -import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.android.tv.MainActivity; import com.android.tv.R; -import com.android.tv.dvr.DvrStorageStatusManager; import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelWatchConflictFragment; import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment; import com.android.tv.guide.ProgramGuide; -import java.util.List; - public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { - /** - * Key for input ID. - * Type: String. - */ + /** Key for input ID. Type: String. */ public static final String KEY_INPUT_ID = "DvrHalfSizedDialogFragment.input_id"; - /** - * Key for the program. - * Type: {@link com.android.tv.data.Program}. - */ + /** Key for the program. Type: {@link com.android.tv.data.Program}. */ public static final String KEY_PROGRAM = "DvrHalfSizedDialogFragment.program"; - /** - * Key for the channel ID. - * Type: long. - */ + /** Key for the channel ID. Type: long. */ public static final String KEY_CHANNEL_ID = "DvrHalfSizedDialogFragment.channel_id"; - /** - * Key for the recording start time in millisecond. - * Type: long. - */ + /** Key for the recording start time in millisecond. Type: long. */ public static final String KEY_START_TIME_MS = "DvrHalfSizedDialogFragment.start_time_ms"; - /** - * Key for the recording end time in millisecond. - * Type: long. - */ + /** Key for the recording end time in millisecond. Type: long. */ public static final String KEY_END_TIME_MS = "DvrHalfSizedDialogFragment.end_time_ms"; @Override @@ -93,14 +72,14 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { private DvrGuidedStepFragment mFragment; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); mFragment = onCreateGuidedStepFragment(); mFragment.setArguments(getArguments()); mFragment.setOnActionClickListener(getOnActionClickListener()); - GuidedStepFragment.add(getChildFragmentManager(), - mFragment, R.id.halfsized_dialog_host); + GuidedStepFragment.add( + getChildFragmentManager(), mFragment, R.id.halfsized_dialog_host); return view; } @@ -158,19 +137,15 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { } /** A dialog fragment for {@link DvrMissingStorageErrorFragment}. */ - public static class DvrMissingStorageErrorDialogFragment - extends DvrGuidedStepDialogFragment { + public static class DvrMissingStorageErrorDialogFragment extends DvrGuidedStepDialogFragment { @Override protected DvrGuidedStepFragment onCreateGuidedStepFragment() { return new DvrMissingStorageErrorFragment(); } } - /** - * A dialog fragment to show error message when there is no enough free space to record. - */ - public static class DvrNoFreeSpaceErrorDialogFragment - extends DvrGuidedStepDialogFragment { + /** A dialog fragment to show error message when there is no enough free space to record. */ + public static class DvrNoFreeSpaceErrorDialogFragment extends DvrGuidedStepDialogFragment { @Override protected DvrGuidedStepFragment onCreateGuidedStepFragment() { return new DvrGuidedStepFragment.DvrNoFreeSpaceErrorFragment(); @@ -178,8 +153,7 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { } /** - * A dialog fragment to show error message when the current storage is too small to - * support DVR + * A dialog fragment to show error message when the current storage is too small to support DVR */ public static class DvrSmallSizedStorageErrorDialogFragment extends DvrGuidedStepDialogFragment { @@ -212,4 +186,12 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { return new DvrAlreadyRecordedFragment(); } } -}
\ No newline at end of file + + /** A dialog fragment for {@link DvrFutureProgramInfoFragment}. */ + public static class DvrFutureProgramInfoDialogFragment extends DvrGuidedStepDialogFragment { + @Override + protected DvrGuidedStepFragment onCreateGuidedStepFragment() { + return new DvrFutureProgramInfoFragment(); + } + } +} diff --git a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java index 182416b6..6fba4d98 100644 --- a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java +++ b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java @@ -22,19 +22,15 @@ import android.content.Intent; import android.os.Bundle; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.ui.browse.DvrBrowseActivity; - import java.util.ArrayList; import java.util.List; public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { - /** - * Key for the failed scheduled recordings information. - */ + /** Key for the failed scheduled recordings information. */ public static final String FAILED_SCHEDULED_RECORDING_INFOS = "failed_scheduled_recording_infos"; @@ -54,7 +50,8 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { } SoftPreconditions.checkState( mFailedScheduledRecordingInfos != null && !mFailedScheduledRecordingInfos.isEmpty(), - TAG, "failed scheduled recording is null"); + TAG, + "failed scheduled recording is null"); } @Override @@ -63,28 +60,39 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { String description; int failedScheduledRecordingSize = mFailedScheduledRecordingInfos.size(); if (failedScheduledRecordingSize == 1) { - title = getString( - R.string.dvr_error_insufficient_space_title_one_recording, - mFailedScheduledRecordingInfos.get(0)); - description = getString( - R.string.dvr_error_insufficient_space_description_one_recording, - mFailedScheduledRecordingInfos.get(0)); + title = + getString( + R.string.dvr_error_insufficient_space_title_one_recording, + mFailedScheduledRecordingInfos.get(0)); + description = + getString( + R.string.dvr_error_insufficient_space_description_one_recording, + mFailedScheduledRecordingInfos.get(0)); } else if (failedScheduledRecordingSize == 2) { - title = getString( - R.string.dvr_error_insufficient_space_title_two_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1)); - description = getString( - R.string.dvr_error_insufficient_space_description_two_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1)); + title = + getString( + R.string.dvr_error_insufficient_space_title_two_recordings, + mFailedScheduledRecordingInfos.get(0), + mFailedScheduledRecordingInfos.get(1)); + description = + getString( + R.string.dvr_error_insufficient_space_description_two_recordings, + mFailedScheduledRecordingInfos.get(0), + mFailedScheduledRecordingInfos.get(1)); } else { - title = getString( - R.string.dvr_error_insufficient_space_title_three_or_more_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1), - mFailedScheduledRecordingInfos.get(2)); - description = getString( - R.string.dvr_error_insufficient_space_description_three_or_more_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1), - mFailedScheduledRecordingInfos.get(2)); + title = + getString( + R.string.dvr_error_insufficient_space_title_three_or_more_recordings, + mFailedScheduledRecordingInfos.get(0), + mFailedScheduledRecordingInfos.get(1), + mFailedScheduledRecordingInfos.get(2)); + description = + getString( + R.string + .dvr_error_insufficient_space_description_three_or_more_recordings, + mFailedScheduledRecordingInfos.get(0), + mFailedScheduledRecordingInfos.get(1), + mFailedScheduledRecordingInfos.get(2)); } return new Guidance(title, description, null, null); } @@ -92,15 +100,18 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .clickAction(GuidedAction.ACTION_ID_OK) - .build()); - if (TvApplication.getSingletons(getContext()).getDvrManager().hasValidItems()) { - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_VIEW_RECENT_RECORDINGS) - .title(getResources().getString( - R.string.dvr_error_insufficient_space_action_view_recent_recordings)) - .build()); + actions.add( + new GuidedAction.Builder(activity).clickAction(GuidedAction.ACTION_ID_OK).build()); + if (TvSingletons.getSingletons(getContext()).getDvrManager().hasValidItems()) { + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_VIEW_RECENT_RECORDINGS) + .title( + getResources() + .getString( + R.string + .dvr_error_insufficient_space_action_view_recent_recordings)) + .build()); } } diff --git a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java index e726995f..e5f40260 100644 --- a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java +++ b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java @@ -24,10 +24,8 @@ import android.provider.Settings; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.util.Log; - import com.android.tv.R; import com.android.tv.dvr.ui.browse.DvrDetailsActivity; - import java.util.List; public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment { @@ -44,22 +42,24 @@ public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment { @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { String title = getResources().getString(R.string.dvr_error_missing_storage_title); - String description = getResources().getString( - R.string.dvr_error_missing_storage_description); + String description = + getResources().getString(R.string.dvr_error_missing_storage_description); return new Guidance(title, description, null, null); } @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_OK) - .title(android.R.string.ok) - .build()); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_OPEN_STORAGE_SETTINGS) - .title(getResources().getString(R.string.dvr_action_error_storage_settings)) - .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_OK) + .title(android.R.string.ok) + .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_OPEN_STORAGE_SETTINGS) + .title(getResources().getString(R.string.dvr_action_error_storage_settings)) + .build()); } @Override diff --git a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java b/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java index e4cb7243..5bb97e90 100644 --- a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java +++ b/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java @@ -16,9 +16,11 @@ package com.android.tv.dvr.ui; +import android.annotation.TargetApi; import android.app.FragmentManager; import android.content.Context; import android.graphics.Typeface; +import android.os.Build; import android.os.Bundle; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; @@ -26,23 +28,20 @@ import android.support.v17.leanback.widget.GuidedActionsStylist; import android.view.View; import android.widget.ImageView; import android.widget.TextView; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.data.SeriesRecording; - import java.util.ArrayList; import java.util.List; /** Fragment for DVR series recording settings. */ +@TargetApi(Build.VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment { - /** - * Name of series recording id starting the fragment. - * Type: Long - */ + /** Name of series recording id starting the fragment. Type: Long */ public static final String COME_FROM_SERIES_RECORDING_ID = "series_recording_id"; private static final int ONE_TIME_RECORDING_ID = 0; @@ -61,14 +60,14 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment { public void onAttach(Context context) { super.onAttach(context); mSeriesRecordings.clear(); - mSeriesRecordings.add(new SeriesRecording.Builder() - .setTitle(getString(R.string.dvr_priority_action_one_time_recording)) - .setPriority(Long.MAX_VALUE) - .setId(ONE_TIME_RECORDING_ID) - .build()); - DvrDataManager dvrDataManager = TvApplication.getSingletons(context).getDvrDataManager(); - long comeFromSeriesRecordingId = - getArguments().getLong(COME_FROM_SERIES_RECORDING_ID, -1); + mSeriesRecordings.add( + new SeriesRecording.Builder() + .setTitle(getString(R.string.dvr_priority_action_one_time_recording)) + .setPriority(Long.MAX_VALUE) + .setId(ONE_TIME_RECORDING_ID) + .build()); + DvrDataManager dvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager(); + long comeFromSeriesRecordingId = getArguments().getLong(COME_FROM_SERIES_RECORDING_ID, -1); for (SeriesRecording series : dvrDataManager.getSeriesRecordings()) { if (series.getState() == SeriesRecording.STATE_SERIES_NORMAL || series.getId() == comeFromSeriesRecordingId) { @@ -86,52 +85,62 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment { @Override public void onResume() { super.onResume(); - setSelectedActionPosition(mComeFromSeriesRecording == null ? 1 - : mSeriesRecordings.indexOf(mComeFromSeriesRecording)); + setSelectedActionPosition( + mComeFromSeriesRecording == null + ? 1 + : mSeriesRecordings.indexOf(mComeFromSeriesRecording)); } @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { - String breadcrumb = mComeFromSeriesRecording == null ? null - : mComeFromSeriesRecording.getTitle(); - return new Guidance(getString(R.string.dvr_priority_title), - getString(R.string.dvr_priority_description), breadcrumb, null); + String breadcrumb = + mComeFromSeriesRecording == null ? null : mComeFromSeriesRecording.getTitle(); + return new Guidance( + getString(R.string.dvr_priority_title), + getString(R.string.dvr_priority_description), + breadcrumb, + null); } @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { int position = 0; for (SeriesRecording seriesRecording : mSeriesRecordings) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(position++) - .title(seriesRecording.getTitle()) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(position++) + .title(seriesRecording.getTitle()) + .build()); } } @Override public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_SAVE) - .title(getString(R.string.dvr_priority_button_action_save)) - .build()); - actions.add(new GuidedAction.Builder(getActivity()) - .clickAction(GuidedAction.ACTION_ID_CANCEL) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_SAVE) + .title(getString(R.string.dvr_priority_button_action_save)) + .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .clickAction(GuidedAction.ACTION_ID_CANCEL) + .build()); } @Override public void onTrackedGuidedActionClicked(GuidedAction action) { long actionId = action.getId(); if (actionId == ACTION_ID_SAVE) { - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); int size = mSeriesRecordings.size(); for (int i = 1; i < size; ++i) { long priority = DvrScheduleManager.suggestSeriesPriority(size - i); SeriesRecording seriesRecording = mSeriesRecordings.get(i); if (seriesRecording.getPriority() != priority) { - dvrManager.updateSeriesRecording(SeriesRecording.buildFrom(seriesRecording) - .setPriority(priority).build()); + dvrManager.updateSeriesRecording( + SeriesRecording.buildFrom(seriesRecording) + .setPriority(priority) + .build()); } } FragmentManager fragmentManager = getFragmentManager(); @@ -222,8 +231,9 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment { private void updateItem(View itemView, int position) { GuidedAction action = getActions().get(position); action.setTitle(mSeriesRecordings.get(position).getTitle()); - boolean selected = mSelectedRecording != null - && mSeriesRecordings.indexOf(mSelectedRecording) == position; + boolean selected = + mSelectedRecording != null + && mSeriesRecordings.indexOf(mSelectedRecording) == position; TextView titleView = (TextView) itemView.findViewById(R.id.guidedactions_item_title); ImageView imageView = (ImageView) itemView.findViewById(R.id.guidedactions_item_tail_image); if (position == 0) { @@ -259,4 +269,4 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment { titleView.setTypeface(titleView.getTypeface(), Typeface.NORMAL); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java index 390e0928..5251e140 100644 --- a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java +++ b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java @@ -26,9 +26,8 @@ import android.support.v17.leanback.app.GuidedStepFragment; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.text.format.DateUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; @@ -36,21 +35,17 @@ import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment; import com.android.tv.util.Utils; - import java.util.Collections; import java.util.List; /** * A fragment which asks the user the type of the recording. - * <p> - * The program should be episodic and the series recording should not had been created yet. + * + * <p>The program should be episodic and the series recording should not had been created yet. */ @TargetApi(Build.VERSION_CODES.N) public class DvrScheduleFragment extends DvrGuidedStepFragment { - /** - * Key for the whether to add the current program to series. - * Type: boolean - */ + /** Key for the whether to add the current program to series. Type: boolean */ public static final String KEY_ADD_CURRENT_PROGRAM_TO_SERIES = "add_current_program_to_series"; private static final String TAG = "DvrScheduleFragment"; @@ -68,13 +63,18 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment { mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); mAddCurrentProgramToSeries = args.getBoolean(KEY_ADD_CURRENT_PROGRAM_TO_SERIES, false); } - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); - SoftPreconditions.checkArgument(mProgram != null && mProgram.isEpisodic(), TAG, - "The program should be episodic: " + mProgram); + DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); + SoftPreconditions.checkArgument( + mProgram != null && mProgram.isEpisodic(), + TAG, + "The program should be episodic: %s ", + mProgram); SeriesRecording seriesRecording = dvrManager.getSeriesRecording(mProgram); - SoftPreconditions.checkArgument(seriesRecording == null - || seriesRecording.isStopped(), TAG, - "The series recording should be stopped or null: " + seriesRecording); + SoftPreconditions.checkArgument( + seriesRecording == null || seriesRecording.isStopped(), + TAG, + "The series recording should be stopped or null: %s", + seriesRecording); super.onCreate(savedInstanceState); } @@ -96,23 +96,33 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment { Context context = getContext(); String description; if (mProgram.getStartTimeUtcMillis() <= System.currentTimeMillis()) { - description = getString(R.string.dvr_action_record_episode_from_now_description, - DateUtils.formatDateTime(context, mProgram.getEndTimeUtcMillis(), - DateUtils.FORMAT_SHOW_TIME)); + description = + getString( + R.string.dvr_action_record_episode_from_now_description, + DateUtils.formatDateTime( + context, + mProgram.getEndTimeUtcMillis(), + DateUtils.FORMAT_SHOW_TIME)); } else { - description = Utils.getDurationString(context, mProgram.getStartTimeUtcMillis(), - mProgram.getEndTimeUtcMillis(), true); + description = + Utils.getDurationString( + context, + mProgram.getStartTimeUtcMillis(), + mProgram.getEndTimeUtcMillis(), + true); } - actions.add(new GuidedAction.Builder(context) - .id(ACTION_RECORD_EPISODE) - .title(R.string.dvr_action_record_episode) - .description(description) - .build()); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_RECORD_SERIES) - .title(R.string.dvr_action_record_series) - .description(mProgram.getTitle()) - .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_RECORD_EPISODE) + .title(R.string.dvr_action_record_episode) + .description(description) + .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_RECORD_SERIES) + .title(R.string.dvr_action_record_series) + .description(mProgram.getTitle()) + .build()); } @Override @@ -121,34 +131,50 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment { getDvrManager().addSchedule(mProgram); List<ScheduledRecording> conflicts = getDvrManager().getConflictingSchedules(mProgram); if (conflicts.isEmpty()) { - DvrUiHelper.showAddScheduleToast(getContext(), mProgram.getTitle(), - mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis()); + DvrUiHelper.showAddScheduleToast( + getContext(), + mProgram.getTitle(), + mProgram.getStartTimeUtcMillis(), + mProgram.getEndTimeUtcMillis()); dismissDialog(); } else { GuidedStepFragment fragment = new DvrProgramConflictFragment(); Bundle args = new Bundle(); args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, mProgram); fragment.setArguments(args); - GuidedStepFragment.add(getFragmentManager(), fragment, - R.id.halfsized_dialog_host); + GuidedStepFragment.add(getFragmentManager(), fragment, R.id.halfsized_dialog_host); } } else if (action.getId() == ACTION_RECORD_SERIES) { - SeriesRecording seriesRecording = TvApplication.getSingletons(getContext()) - .getDvrDataManager().getSeriesRecording(mProgram.getSeriesId()); + SeriesRecording seriesRecording = + TvSingletons.getSingletons(getContext()) + .getDvrDataManager() + .getSeriesRecording(mProgram.getSeriesId()); if (seriesRecording == null) { - seriesRecording = getDvrManager().addSeriesRecording(mProgram, - Collections.emptyList(), SeriesRecording.STATE_SERIES_STOPPED); + seriesRecording = + getDvrManager() + .addSeriesRecording( + mProgram, + Collections.emptyList(), + SeriesRecording.STATE_SERIES_STOPPED); } else { // Reset priority to the highest. - seriesRecording = SeriesRecording.buildFrom(seriesRecording) - .setPriority(TvApplication.getSingletons(getContext()) - .getDvrScheduleManager().suggestNewSeriesPriority()) - .build(); + seriesRecording = + SeriesRecording.buildFrom(seriesRecording) + .setPriority( + TvSingletons.getSingletons(getContext()) + .getDvrScheduleManager() + .suggestNewSeriesPriority()) + .build(); getDvrManager().updateSeriesRecording(seriesRecording); } - DvrUiHelper.startSeriesSettingsActivity(getContext(), - seriesRecording.getId(), null, true, true, true, + DvrUiHelper.startSeriesSettingsActivity( + getContext(), + seriesRecording.getId(), + null, + true, + true, + true, mAddCurrentProgramToSeries ? mProgram : null); dismissDialog(); } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java index 667af34a..a2ae1f97 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java @@ -19,22 +19,17 @@ package com.android.tv.dvr.ui; import android.app.Activity; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; -/** - * Activity to show details view in DVR. - */ +/** Activity to show details view in DVR. */ public class DvrSeriesDeletionActivity extends Activity { - /** - * Name of series id added to the Intent. - */ + /** Name of series id added to the Intent. */ public static final String SERIES_RECORDING_ID = "series_recording_id"; @Override public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_dvr_series_settings); // Check savedInstanceState to prevent that activity is being showed with animation. diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java index 8bf8560f..685f0a58 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java @@ -26,9 +26,8 @@ import android.support.v17.leanback.widget.GuidedActionsStylist; import android.text.TextUtils; import android.view.ViewGroup.LayoutParams; import android.widget.Toast; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; @@ -37,7 +36,6 @@ import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.ui.GuidedActionsStylistWithDivider; import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -45,9 +43,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -/** - * Fragment for DVR series recording settings. - */ +/** Fragment for DVR series recording settings. */ public class DvrSeriesDeletionFragment extends GuidedStepFragment { private static final long WATCHED_TIME_UNIT_THRESHOLD = TimeUnit.MINUTES.toMillis(2); @@ -68,18 +64,23 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { @Override public void onAttach(Context context) { super.onAttach(context); - mSeriesRecordingId = getArguments() - .getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1); + mSeriesRecordingId = + getArguments().getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1); SoftPreconditions.checkArgument(mSeriesRecordingId != -1); - mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager(); + mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager(); mDvrWatchedPositionManager = - TvApplication.getSingletons(context).getDvrWatchedPositionManager(); + TvSingletons.getSingletons(context).getDvrWatchedPositionManager(); mRecordings = mDvrDataManager.getRecordedPrograms(mSeriesRecordingId); - mOneLineActionHeight = getResources().getDimensionPixelSize( - R.dimen.dvr_settings_one_line_action_container_height); + mOneLineActionHeight = + getResources() + .getDimensionPixelSize( + R.dimen.dvr_settings_one_line_action_container_height); if (mRecordings.isEmpty()) { - Toast.makeText(getActivity(), getString(R.string.dvr_series_deletion_no_recordings), - Toast.LENGTH_LONG).show(); + Toast.makeText( + getActivity(), + getString(R.string.dvr_series_deletion_no_recordings), + Toast.LENGTH_LONG) + .show(); finishGuidedStepFragments(); return; } @@ -93,28 +94,35 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { if (series != null) { breadcrumb = series.getTitle(); } - return new Guidance(getString(R.string.dvr_series_deletion_title), - getString(R.string.dvr_series_deletion_description), breadcrumb, null); + return new Guidance( + getString(R.string.dvr_series_deletion_title), + getString(R.string.dvr_series_deletion_description), + breadcrumb, + null); } @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_SELECT_WATCHED) - .title(getString(R.string.dvr_series_select_watched)) - .build()); - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_SELECT_ALL) - .title(getString(R.string.dvr_series_select_all)) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_SELECT_WATCHED) + .title(getString(R.string.dvr_series_select_watched)) + .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_SELECT_ALL) + .title(getString(R.string.dvr_series_select_all)) + .build()); actions.add(GuidedActionsStylistWithDivider.createDividerAction(getContext())); for (RecordedProgram recording : mRecordings) { long watchedPositionMs = mDvrWatchedPositionManager.getWatchedPosition(recording.getId()); String title = recording.getEpisodeDisplayTitle(getContext()); if (TextUtils.isEmpty(title)) { - title = TextUtils.isEmpty(recording.getTitle()) ? - getString(R.string.channel_banner_no_title) : recording.getTitle(); + title = + TextUtils.isEmpty(recording.getTitle()) + ? getString(R.string.channel_banner_no_title) + : recording.getTitle(); } String description; if (watchedPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { @@ -123,24 +131,27 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { } else { description = getString(R.string.dvr_series_never_watched); } - actions.add(new GuidedAction.Builder(getActivity()) - .id(recording.getId()) - .title(title) - .description(description) - .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(recording.getId()) + .title(title) + .description(description) + .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID) + .build()); } } @Override public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_DELETE) - .title(getString(R.string.dvr_detail_delete)) - .build()); - actions.add(new GuidedAction.Builder(getActivity()) - .clickAction(GuidedAction.ACTION_ID_CANCEL) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_DELETE) + .title(getString(R.string.dvr_detail_delete)) + .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .clickAction(GuidedAction.ACTION_ID_CANCEL) + .build()); } @Override @@ -155,12 +166,19 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { } } if (!idsToDelete.isEmpty()) { - DvrManager dvrManager = TvApplication.getSingletons(getActivity()).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(getActivity()).getDvrManager(); dvrManager.removeRecordedPrograms(idsToDelete); } - Toast.makeText(getContext(), getResources().getQuantityString( - R.plurals.dvr_msg_episodes_deleted, idsToDelete.size(), idsToDelete.size(), - mRecordings.size()), Toast.LENGTH_LONG).show(); + Toast.makeText( + getContext(), + getResources() + .getQuantityString( + R.plurals.dvr_msg_episodes_deleted, + idsToDelete.size(), + idsToDelete.size(), + mRecordings.size()), + Toast.LENGTH_LONG) + .show(); finishGuidedStepFragments(); } else if (actionId == GuidedAction.ACTION_ID_CANCEL) { finishGuidedStepFragments(); @@ -218,13 +236,17 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { private String getWatchedString(long watchedPositionMs, long durationMs) { if (durationMs > WATCHED_TIME_UNIT_THRESHOLD) { - return getResources().getString(R.string.dvr_series_watched_info_minutes, - Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)), - Utils.getRoundOffMinsFromMs(durationMs)); + return getResources() + .getString( + R.string.dvr_series_watched_info_minutes, + Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)), + Utils.getRoundOffMinsFromMs(durationMs)); } else { - return getResources().getString(R.string.dvr_series_watched_info_seconds, - Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)), - TimeUnit.MILLISECONDS.toSeconds(durationMs)); + return getResources() + .getString( + R.string.dvr_series_watched_info_seconds, + Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)), + TimeUnit.MILLISECONDS.toSeconds(durationMs)); } } @@ -246,8 +268,10 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { } private void updateSelectAllState(GuidedAction selectAll, boolean select) { - selectAll.setTitle(select ? getString(R.string.dvr_series_deselect_all) - : getString(R.string.dvr_series_select_all)); + selectAll.setTitle( + select + ? getString(R.string.dvr_series_deselect_all) + : getString(R.string.dvr_series_select_all)); notifyActionChanged(findActionPositionById(ACTION_ID_SELECT_ALL)); } } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java index 1a0d13d3..9acb5b5e 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java @@ -19,18 +19,13 @@ package com.android.tv.dvr.ui; import android.app.Activity; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; - import com.android.tv.R; public class DvrSeriesScheduledDialogActivity extends Activity { - /** - * Name of series recording id added to the Intent. - */ + /** Name of series recording id added to the Intent. */ public static final String SERIES_RECORDING_ID = "series_recording_id"; - /** - * Name of flag to check if the dialog should show view schedule option. - */ + /** Name of flag to check if the dialog should show view schedule option. */ public static final String SHOW_VIEW_SCHEDULE_OPTION = "show_view_schedule_option"; @Override @@ -41,8 +36,8 @@ public class DvrSeriesScheduledDialogActivity extends Activity { DvrSeriesScheduledFragment dvrSeriesScheduledFragment = new DvrSeriesScheduledFragment(); dvrSeriesScheduledFragment.setArguments(getIntent().getExtras()); - GuidedStepFragment.addAsRoot(this, dvrSeriesScheduledFragment, - R.id.halfsized_dialog_host); + GuidedStepFragment.addAsRoot( + this, dvrSeriesScheduledFragment, R.id.halfsized_dialog_host); } } } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java index 2c4bb3ea..edb62c96 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java @@ -22,28 +22,26 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v17.leanback.widget.GuidanceStylist; import android.support.v17.leanback.widget.GuidedAction; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.Program; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.ui.list.DvrSchedulesActivity; import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment; - import java.util.List; public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { /** - * The key for program list which will be passed to {@link DvrSeriesSchedulesFragment}. - * Type: List<{@link Program}> + * The key for program list which will be passed to {@link DvrSeriesSchedulesFragment}. Type: + * List<{@link Program}> */ public static final String SERIES_SCHEDULED_KEY_PROGRAMS = "series_scheduled_key_programs"; - private final static long SERIES_RECORDING_ID_NOT_SET = -1; + private static final long SERIES_RECORDING_ID_NOT_SET = -1; - private final static int ACTION_VIEW_SCHEDULES = 1; + private static final int ACTION_VIEW_SCHEDULES = 1; private SeriesRecording mSeriesRecording; private boolean mShowViewScheduleOption; @@ -57,26 +55,35 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { @Override public void onAttach(Context context) { super.onAttach(context); - long seriesRecordingId = getArguments().getLong( - DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, SERIES_RECORDING_ID_NOT_SET); + long seriesRecordingId = + getArguments() + .getLong( + DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, + SERIES_RECORDING_ID_NOT_SET); if (seriesRecordingId == SERIES_RECORDING_ID_NOT_SET) { getActivity().finish(); return; } - mShowViewScheduleOption = getArguments().getBoolean( - DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION); - mSeriesRecording = TvApplication.getSingletons(context).getDvrDataManager() - .getSeriesRecording(seriesRecordingId); + mShowViewScheduleOption = + getArguments() + .getBoolean(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION); + mSeriesRecording = + TvSingletons.getSingletons(context) + .getDvrDataManager() + .getSeriesRecording(seriesRecordingId); if (mSeriesRecording == null) { getActivity().finish(); return; } mPrograms = (List<Program>) BigArguments.getArgument(SERIES_SCHEDULED_KEY_PROGRAMS); BigArguments.reset(); - mSchedulesAddedCount = TvApplication.getSingletons(getContext()).getDvrManager() - .getAvailableScheduledRecording(mSeriesRecording.getId()).size(); + mSchedulesAddedCount = + TvSingletons.getSingletons(getContext()) + .getDvrManager() + .getAvailableScheduledRecording(mSeriesRecording.getId()) + .size(); DvrScheduleManager dvrScheduleManager = - TvApplication.getSingletons(context).getDvrScheduleManager(); + TvSingletons.getSingletons(context).getDvrScheduleManager(); List<ScheduledRecording> conflictingRecordings = dvrScheduleManager.getConflictingSchedules(mSeriesRecording); mHasConflict = !conflictingRecordings.isEmpty(); @@ -87,7 +94,7 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { ++mOutThisSeriesConflictCount; } } - } + } @Override public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { @@ -104,14 +111,14 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Context context = getContext(); - actions.add(new GuidedAction.Builder(context) - .clickAction(GuidedAction.ACTION_ID_OK) - .build()); + actions.add( + new GuidedAction.Builder(context).clickAction(GuidedAction.ACTION_ID_OK).build()); if (mShowViewScheduleOption) { - actions.add(new GuidedAction.Builder(context) - .id(ACTION_VIEW_SCHEDULES) - .title(R.string.dvr_action_view_schedules) - .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_VIEW_SCHEDULES) + .title(R.string.dvr_action_view_schedules) + .build()); } } @@ -119,13 +126,15 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { public void onTrackedGuidedActionClicked(GuidedAction action) { if (action.getId() == ACTION_VIEW_SCHEDULES) { Intent intent = new Intent(getActivity(), DvrSchedulesActivity.class); - intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity - .TYPE_SERIES_SCHEDULE); - intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, + intent.putExtra( + DvrSchedulesActivity.KEY_SCHEDULES_TYPE, + DvrSchedulesActivity.TYPE_SERIES_SCHEDULE); + intent.putExtra( + DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, mSeriesRecording); BigArguments.reset(); - BigArguments.setArgument(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, mPrograms); + BigArguments.setArgument( + DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, mPrograms); startActivity(intent); } getActivity().finish(); @@ -148,33 +157,47 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { private String getDescription() { if (!mHasConflict) { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_no_conflict, mSchedulesAddedCount, - mSchedulesAddedCount, mSeriesRecording.getTitle()); + return getResources() + .getQuantityString( + R.plurals.dvr_series_scheduled_no_conflict, + mSchedulesAddedCount, + mSchedulesAddedCount, + mSeriesRecording.getTitle()); } else { // mInThisSeriesConflictCount equals 0 and mOutThisSeriesConflictCount equals 0 means // mHasConflict is false. So we don't need to check that case. if (mInThisSeriesConflictCount != 0 && mOutThisSeriesConflictCount != 0) { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_this_and_other_series_conflict, - mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), - mInThisSeriesConflictCount + mOutThisSeriesConflictCount); + return getResources() + .getQuantityString( + R.plurals.dvr_series_scheduled_this_and_other_series_conflict, + mSchedulesAddedCount, + mSchedulesAddedCount, + mSeriesRecording.getTitle(), + mInThisSeriesConflictCount + mOutThisSeriesConflictCount); } else if (mInThisSeriesConflictCount != 0) { - return getResources().getQuantityString( - R.plurals.dvr_series_recording_scheduled_only_this_series_conflict, - mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), - mInThisSeriesConflictCount); + return getResources() + .getQuantityString( + R.plurals.dvr_series_recording_scheduled_only_this_series_conflict, + mSchedulesAddedCount, + mSchedulesAddedCount, + mSeriesRecording.getTitle(), + mInThisSeriesConflictCount); } else { if (mOutThisSeriesConflictCount == 1) { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_only_other_series_one_conflict, - mSchedulesAddedCount, mSchedulesAddedCount, - mSeriesRecording.getTitle()); + return getResources() + .getQuantityString( + R.plurals.dvr_series_scheduled_only_other_series_one_conflict, + mSchedulesAddedCount, + mSchedulesAddedCount, + mSeriesRecording.getTitle()); } else { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_only_other_series_many_conflicts, - mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), - mOutThisSeriesConflictCount); + return getResources() + .getQuantityString( + R.plurals.dvr_series_scheduled_only_other_series_many_conflicts, + mSchedulesAddedCount, + mSchedulesAddedCount, + mSeriesRecording.getTitle(), + mOutThisSeriesConflictCount); } } } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java index 6dd20b3a..1a51cf46 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java @@ -20,34 +20,27 @@ import android.app.Activity; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; import com.android.tv.common.SoftPreconditions; -/** - * Activity to show details view in DVR. - */ +/** Activity to show details view in DVR. */ public class DvrSeriesSettingsActivity extends Activity { - /** - * Name of series id added to the Intent. - * Type: Long - */ + /** Name of series id added to the Intent. Type: Long */ public static final String SERIES_RECORDING_ID = "series_recording_id"; /** * Name of the boolean flag to decide if the series recording with empty schedule and recording - * will be removed. - * Type: boolean + * will be removed. Type: boolean */ public static final String REMOVE_EMPTY_SERIES_RECORDING = "remove_empty_series_recording"; /** - * Name of the boolean flag to decide if the setting fragment should be translucent. - * Type: boolean + * Name of the boolean flag to decide if the setting fragment should be translucent. Type: + * boolean */ public static final String IS_WINDOW_TRANSLUCENT = "windows_translucent"; /** - * Name of the program list. The list contains the programs which belong to the series. - * Type: List<{@link com.android.tv.data.Program}> + * Name of the program list. The list contains the programs which belong to the series. Type: + * List<{@link com.android.tv.data.Program}> */ public static final String PROGRAM_LIST = "program_list"; @@ -67,7 +60,7 @@ public class DvrSeriesSettingsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_dvr_series_settings); long seriesRecordingId = getIntent().getLongExtra(SERIES_RECORDING_ID, -1); @@ -83,8 +76,9 @@ public class DvrSeriesSettingsActivity extends Activity { @Override public void onAttachedToWindow() { if (!getIntent().getExtras().getBoolean(IS_WINDOW_TRANSLUCENT, true)) { - getWindow().setBackgroundDrawable( - new ColorDrawable(getColor(R.color.common_tv_background))); + getWindow() + .setBackgroundDrawable( + new ColorDrawable(getColor(R.color.common_tv_background))); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java index f28382da..eadb3b9e 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java @@ -16,20 +16,22 @@ package com.android.tv.dvr.ui; +import android.annotation.TargetApi; import android.app.FragmentManager; import android.content.Context; +import android.os.Build; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.support.v17.leanback.widget.GuidedActionsStylist; import android.util.LongSparseArray; - import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.data.Channel; +import com.android.tv.TvSingletons; import com.android.tv.data.ChannelDataManager; +import com.android.tv.data.ChannelImpl; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.ScheduledRecording; @@ -37,20 +39,18 @@ import com.android.tv.dvr.data.SeasonEpisodeNumber; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.data.SeriesRecording.ChannelOption; import com.android.tv.dvr.recorder.SeriesRecordingScheduler; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -/** - * Fragment for DVR series recording settings. - */ +/** Fragment for DVR series recording settings. */ +@TargetApi(Build.VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated public class DvrSeriesSettingsFragment extends GuidedStepFragment implements DvrDataManager.SeriesRecordingListener { private static final String TAG = "SeriesSettingsFragment"; - private static final boolean DEBUG = false; private static final long ACTION_ID_PRIORITY = 10; private static final long ACTION_ID_CHANNEL = 11; @@ -85,19 +85,20 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment public void onAttach(Context context) { super.onAttach(context); mBackStackCount = getFragmentManager().getBackStackEntryCount(); - mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager(); + mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager(); mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID); mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId); if (mSeriesRecording == null) { getActivity().finish(); return; } - mShowViewScheduleOptionInDialog = getArguments().getBoolean( - DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG); + mShowViewScheduleOptionInDialog = + getArguments() + .getBoolean(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG); mCurrentProgram = getArguments().getParcelable(DvrSeriesSettingsActivity.CURRENT_PROGRAM); mDvrDataManager.addSeriesRecordingListener(this); - mPrograms = (List<Program>) BigArguments.getArgument( - DvrSeriesSettingsActivity.PROGRAM_LIST); + mPrograms = + (List<Program>) BigArguments.getArgument(DvrSeriesSettingsActivity.PROGRAM_LIST); BigArguments.reset(); if (mPrograms == null) { getActivity().finish(); @@ -105,7 +106,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } Set<Long> channelIds = new HashSet<>(); ChannelDataManager channelDataManager = - TvApplication.getSingletons(context).getChannelDataManager(); + TvSingletons.getSingletons(context).getChannelDataManager(); for (Program program : mPrograms) { long channelId = program.getChannelId(); if (channelIds.add(channelId)) { @@ -126,7 +127,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL; } } - mChannels.sort(Channel.CHANNEL_NUMBER_COMPARATOR); + mChannels.sort(ChannelImpl.CHANNEL_NUMBER_COMPARATOR); mFragmentTitle = getString(R.string.dvr_series_settings_title); mProrityActionTitle = getString(R.string.dvr_series_settings_priority); mProrityActionHighestText = getString(R.string.dvr_series_settings_priority_highest); @@ -150,8 +151,9 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment @Override public void onDestroy() { - if (getFragmentManager().getBackStackEntryCount() == mBackStackCount && getArguments() - .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) { + if (getFragmentManager().getBackStackEntryCount() == mBackStackCount + && getArguments() + .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) { mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeriesRecordingId); } super.onDestroy(); @@ -166,29 +168,33 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment @Override public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { - mPriorityGuidedAction = new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_PRIORITY) - .title(mProrityActionTitle) - .build(); + mPriorityGuidedAction = + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_PRIORITY) + .title(mProrityActionTitle) + .build(); actions.add(mPriorityGuidedAction); - mChannelsGuidedAction = new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_CHANNEL) - .title(mChannelsActionTitle) - .subActions(buildChannelSubAction()) - .build(); + mChannelsGuidedAction = + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_CHANNEL) + .title(mChannelsActionTitle) + .subActions(buildChannelSubAction()) + .build(); actions.add(mChannelsGuidedAction); updateChannelsGuidedAction(false); } @Override public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) { - actions.add(new GuidedAction.Builder(getActivity()) - .clickAction(GuidedAction.ACTION_ID_OK) - .build()); - actions.add(new GuidedAction.Builder(getActivity()) - .clickAction(GuidedAction.ACTION_ID_CANCEL) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .clickAction(GuidedAction.ACTION_ID_OK) + .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .clickAction(GuidedAction.ACTION_ID_CANCEL) + .build()); } @Override @@ -199,16 +205,18 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment || mSeriesRecording.isStopped() || (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE && mSeriesRecording.getChannelId() != mSelectedChannelId)) { - SeriesRecording.Builder builder = SeriesRecording.buildFrom(mSeriesRecording) - .setChannelOption(mChannelOption) - .setState(SeriesRecording.STATE_SERIES_NORMAL); + SeriesRecording.Builder builder = + SeriesRecording.buildFrom(mSeriesRecording) + .setChannelOption(mChannelOption) + .setState(SeriesRecording.STATE_SERIES_NORMAL); if (mSelectedChannelId != Channel.INVALID_ID) { builder.setChannelId(mSelectedChannelId); } - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); - dvrManager.updateSeriesRecording(builder.build()); - if (mCurrentProgram != null && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL - || mSelectedChannelId == mCurrentProgram.getChannelId())) { + DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); + dvrManager.updateSeriesRecording(builder.build()); + if (mCurrentProgram != null + && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL + || mSelectedChannelId == mCurrentProgram.getChannelId())) { dvrManager.addSchedule(mCurrentProgram); } updateSchedulesToSeries(); @@ -222,7 +230,8 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment FragmentManager fragmentManager = getFragmentManager(); DvrPrioritySettingsFragment fragment = new DvrPrioritySettingsFragment(); Bundle args = new Bundle(); - args.putLong(DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID, + args.putLong( + DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID, mSeriesRecording.getId()); fragment.setArguments(args); GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame); @@ -254,9 +263,9 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment private void updateChannelsGuidedAction(boolean notifyActionChanged) { if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) { mChannelsGuidedAction.setDescription(mChannelsActionAllText); - } else if (mId2Channel.get(mSelectedChannelId) != null){ - mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId) - .getDisplayText()); + } else if (mId2Channel.get(mSelectedChannelId) != null) { + mChannelsGuidedAction.setDescription( + mId2Channel.get(mSelectedChannelId).getDisplayText()); } if (notifyActionChanged) { notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL)); @@ -282,8 +291,8 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } else if (priorityOrder >= totalSeriesCount - 1) { mPriorityGuidedAction.setDescription(mProrityActionLowestText); } else { - mPriorityGuidedAction.setDescription(getString( - R.string.dvr_series_settings_priority_rank, priorityOrder + 1)); + mPriorityGuidedAction.setDescription( + getString(R.string.dvr_series_settings_priority_rank, priorityOrder + 1)); } notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY)); } @@ -294,54 +303,66 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment for (ScheduledRecording r : mDvrDataManager.getScheduledRecordings(mSeriesRecordingId)) { if (r.getState() != ScheduledRecording.STATE_RECORDING_FAILED && r.getState() != ScheduledRecording.STATE_RECORDING_CLIPPED) { - scheduledEpisodes.add(new SeasonEpisodeNumber( - r.getSeriesRecordingId(), r.getSeasonNumber(), r.getEpisodeNumber())); + scheduledEpisodes.add( + new SeasonEpisodeNumber( + r.getSeriesRecordingId(), + r.getSeasonNumber(), + r.getEpisodeNumber())); } } for (Program program : mPrograms) { // Removes current programs and scheduled episodes out, matches the channel option. if (program.getStartTimeUtcMillis() >= System.currentTimeMillis() && mSeriesRecording.matchProgram(program) - && !scheduledEpisodes.contains(new SeasonEpisodeNumber( - mSeriesRecordingId, program.getSeasonNumber(), program.getEpisodeNumber()))) { + && !scheduledEpisodes.contains( + new SeasonEpisodeNumber( + mSeriesRecordingId, + program.getSeasonNumber(), + program.getEpisodeNumber()))) { recordingCandidates.add(program); } } if (recordingCandidates.isEmpty()) { return; } - List<Program> programsToSchedule = SeriesRecordingScheduler.pickOneProgramPerEpisode( - mDvrDataManager, Collections.singletonList(mSeriesRecording), recordingCandidates) - .get(mSeriesRecordingId); + List<Program> programsToSchedule = + SeriesRecordingScheduler.pickOneProgramPerEpisode( + mDvrDataManager, + Collections.singletonList(mSeriesRecording), + recordingCandidates) + .get(mSeriesRecordingId); if (!programsToSchedule.isEmpty()) { - TvApplication.getSingletons(getContext()).getDvrManager() + TvSingletons.getSingletons(getContext()) + .getDvrManager() .addScheduleToSeriesRecording(mSeriesRecording, programsToSchedule); } } private List<GuidedAction> buildChannelSubAction() { List<GuidedAction> channelSubActions = new ArrayList<>(); - channelSubActions.add(new GuidedAction.Builder(getActivity()) - .id(SUB_ACTION_ID_CHANNEL_ALL) - .title(mChannelsActionAllText) - .build()); + channelSubActions.add( + new GuidedAction.Builder(getActivity()) + .id(SUB_ACTION_ID_CHANNEL_ALL) + .title(mChannelsActionAllText) + .build()); for (Channel channel : mChannels) { - channelSubActions.add(new GuidedAction.Builder(getActivity()) - .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId()) - .title(channel.getDisplayText()) - .build()); + channelSubActions.add( + new GuidedAction.Builder(getActivity()) + .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId()) + .title(channel.getDisplayText()) + .build()); } return channelSubActions; } private void showConfirmDialog() { - DvrUiHelper.StartSeriesScheduledDialogActivity(getContext(), mSeriesRecording, - mShowViewScheduleOptionInDialog, mPrograms); + DvrUiHelper.startSeriesScheduledDialogActivity( + getContext(), mSeriesRecording, mShowViewScheduleOptionInDialog, mPrograms); finishGuidedStepFragments(); } @Override - public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { } + public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {} @Override public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { @@ -363,4 +384,4 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java index baa45793..e93387ab 100644 --- a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java +++ b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java @@ -25,45 +25,36 @@ import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.data.ScheduledRecording; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; /** * A fragment which asks the user to make a recording schedule for the program. - * <p> - * If the program belongs to a series and the series recording is not created yet, we will show the - * option to record all the episodes of the series. + * + * <p>If the program belongs to a series and the series recording is not created yet, we will show + * the option to record all the episodes of the series. */ @TargetApi(Build.VERSION_CODES.N) public class DvrStopRecordingFragment extends DvrGuidedStepFragment { - /** - * The action ID for the stop action. - */ + /** The action ID for the stop action. */ public static final int ACTION_STOP = 1; - /** - * Key for the program. - * Type: {@link com.android.tv.data.Program}. - */ + /** Key for the program. Type: {@link com.android.tv.data.Program}. */ public static final String KEY_REASON = "DvrStopRecordingFragment.type"; @Retention(RetentionPolicy.SOURCE) @IntDef({REASON_USER_STOP, REASON_ON_CONFLICT}) public @interface ReasonType {} - /** - * The dialog is shown because users want to stop some currently recording program. - */ + /** The dialog is shown because users want to stop some currently recording program. */ public static final int REASON_USER_STOP = 1; /** - * The dialog is shown because users want to record some program that is conflict to the - * current recording program. + * The dialog is shown because users want to record some program that is conflict to the current + * recording program. */ public static final int REASON_ON_CONFLICT = 2; @@ -74,7 +65,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { private final ScheduledRecordingListener mScheduledRecordingListener = new ScheduledRecordingListener() { @Override - public void onScheduledRecordingAdded(ScheduledRecording... schedules) { } + public void onScheduledRecordingAdded(ScheduledRecording... schedules) {} @Override public void onScheduledRecordingRemoved(ScheduledRecording... schedules) { @@ -91,7 +82,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { for (ScheduledRecording schedule : schedules) { if (schedule.getId() == mSchedule.getId() && schedule.getState() - != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { + != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { dismissDialog(); return; } @@ -109,7 +100,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { dismissDialog(); return; } - mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager(); + mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager(); mDvrDataManager.addScheduledRecordingListener(mScheduledRecordingListener); mStopReason = args.getInt(KEY_REASON); } @@ -128,8 +119,10 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { String title = getString(R.string.dvr_stop_recording_dialog_title); String description; if (mStopReason == REASON_ON_CONFLICT) { - description = getString(R.string.dvr_stop_recording_dialog_description_on_conflict, - mSchedule.getProgramDisplayTitle(getContext())); + description = + getString( + R.string.dvr_stop_recording_dialog_description_on_conflict, + mSchedule.getProgramDisplayTitle(getContext())); } else { description = getString(R.string.dvr_stop_recording_dialog_description); } @@ -140,13 +133,15 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { Context context = getContext(); - actions.add(new GuidedAction.Builder(context) - .id(ACTION_STOP) - .title(R.string.dvr_action_stop) - .build()); - actions.add(new GuidedAction.Builder(context) - .clickAction(GuidedAction.ACTION_ID_CANCEL) - .build()); + actions.add( + new GuidedAction.Builder(context) + .id(ACTION_STOP) + .title(R.string.dvr_action_stop) + .build()); + actions.add( + new GuidedAction.Builder(context) + .clickAction(GuidedAction.ACTION_ID_CANCEL) + .build()); } @Override @@ -163,4 +158,4 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { return super.getTrackerLabelForGuidedAction(action); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java index 5b880bd6..15abf902 100644 --- a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java +++ b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java @@ -22,18 +22,15 @@ import android.support.v17.leanback.app.GuidedStepFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.android.tv.R; -/** - * A dialog fragment which contains {@link DvrStopSeriesRecordingFragment}. - */ +/** A dialog fragment which contains {@link DvrStopSeriesRecordingFragment}. */ public class DvrStopSeriesRecordingDialogFragment extends DialogFragment { public static final String DIALOG_TAG = "dialog_tag"; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.halfsized_dialog, container, false); GuidedStepFragment fragment = new DvrStopSeriesRecordingFragment(); fragment.setArguments(getArguments()); diff --git a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java index 7b56cfc1..99211fdb 100644 --- a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java +++ b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java @@ -25,25 +25,18 @@ import android.support.v17.leanback.widget.GuidedAction; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; - import java.util.ArrayList; import java.util.List; -/** - * A fragment which asks the user to stop series recording. - */ +/** A fragment which asks the user to stop series recording. */ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment { - /** - * Key for the series recording to be stopped. - */ + /** Key for the series recording to be stopped. */ public static final String KEY_SERIES_RECORDING = "key_series_recoridng"; private static final int ACTION_STOP_SERIES_RECORDING = 1; @@ -51,7 +44,8 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment { private SeriesRecording mSeriesRecording; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mSeriesRecording = getArguments().getParcelable(KEY_SERIES_RECORDING); return super.onCreateView(inflater, container, savedInstanceState); } @@ -68,19 +62,21 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment { @Override public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_STOP_SERIES_RECORDING) - .title(R.string.dvr_series_schedules_stop_dialog_action_stop) - .build()); - actions.add(new GuidedAction.Builder(activity) - .clickAction(GuidedAction.ACTION_ID_CANCEL) - .build()); + actions.add( + new GuidedAction.Builder(activity) + .id(ACTION_STOP_SERIES_RECORDING) + .title(R.string.dvr_series_schedules_stop_dialog_action_stop) + .build()); + actions.add( + new GuidedAction.Builder(activity) + .clickAction(GuidedAction.ACTION_ID_CANCEL) + .build()); } @Override public void onTrackedGuidedActionClicked(GuidedAction action) { if (action.getId() == ACTION_STOP_SERIES_RECORDING) { - ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + TvSingletons singletons = TvSingletons.getSingletons(getContext()); DvrManager dvrManager = singletons.getDvrManager(); DvrDataManager dataManager = singletons.getDvrDataManager(); List<ScheduledRecording> toDelete = new ArrayList<>(); @@ -96,8 +92,10 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment { if (!toDelete.isEmpty()) { dvrManager.forceRemoveScheduledRecording(ScheduledRecording.toArray(toDelete)); } - dvrManager.updateSeriesRecording(SeriesRecording.buildFrom(mSeriesRecording) - .setState(SeriesRecording.STATE_SERIES_STOPPED).build()); + dvrManager.updateSeriesRecording( + SeriesRecording.buildFrom(mSeriesRecording) + .setState(SeriesRecording.STATE_SERIES_STOPPED) + .build()); } dismissDialog(); } diff --git a/src/com/android/tv/dvr/ui/DvrUiHelper.java b/src/com/android/tv/dvr/ui/DvrUiHelper.java index 302fd6cd..16afbdef 100644 --- a/src/com/android/tv/dvr/ui/DvrUiHelper.java +++ b/src/com/android/tv/dvr/ui/DvrUiHelper.java @@ -37,17 +37,18 @@ import android.text.TextUtils; import android.text.style.TextAppearanceSpan; import android.widget.ImageView; import android.widget.Toast; - import com.android.tv.MainActivity; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; +import com.android.tv.common.BuildConfig; import com.android.tv.common.SoftPreconditions; +import com.android.tv.common.recording.RecordingStorageStatusManager; +import com.android.tv.common.util.CommonUtils; import com.android.tv.data.BaseProgram; -import com.android.tv.data.Channel; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.DvrStorageStatusManager; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; @@ -56,6 +57,7 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyRecordedDialog import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyScheduledDialogFragment; import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelRecordDurationOptionDialogFragment; import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelWatchConflictDialogFragment; +import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrFutureProgramInfoDialogFragment; import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrInsufficientSpaceErrorDialogFragment; import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrMissingStorageErrorDialogFragment; import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrNoFreeSpaceErrorDialogFragment; @@ -65,21 +67,19 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrSmallSizedStorageErro import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrStopRecordingDialogFragment; import com.android.tv.dvr.ui.browse.DvrBrowseActivity; import com.android.tv.dvr.ui.browse.DvrDetailsActivity; +import com.android.tv.dvr.ui.list.DvrHistoryActivity; import com.android.tv.dvr.ui.list.DvrSchedulesActivity; import com.android.tv.dvr.ui.list.DvrSchedulesFragment; import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment; import com.android.tv.dvr.ui.playback.DvrPlaybackActivity; import com.android.tv.util.ToastUtils; import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; -/** - * A helper class for DVR UI. - */ +/** A helper class for DVR UI. */ @MainThread @TargetApi(Build.VERSION_CODES.N) public class DvrUiHelper { @@ -91,45 +91,43 @@ public class DvrUiHelper { * Checks if the storage status is good for recording and shows error messages if needed. * * @param recordingRequestRunnable if the storage status is OK to record or users choose to - * perform the operation anyway, this Runnable will run. + * perform the operation anyway, this Runnable will run. */ - public static void checkStorageStatusAndShowErrorMessage(Activity activity, String inputId, - Runnable recordingRequestRunnable) { - if (Utils.isBundledInput(inputId)) { - switch (TvApplication.getSingletons(activity).getDvrStorageStatusManager() + public static void checkStorageStatusAndShowErrorMessage( + Activity activity, String inputId, Runnable recordingRequestRunnable) { + if (CommonUtils.isBundledInput(inputId)) { + switch (TvSingletons.getSingletons(activity) + .getRecordingStorageStatusManager() .getDvrStorageStatus()) { - case DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL: + case RecordingStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL: showDvrSmallSizedStorageErrorDialog(activity); return; - case DvrStorageStatusManager.STORAGE_STATUS_MISSING: + case RecordingStorageStatusManager.STORAGE_STATUS_MISSING: showDvrMissingStorageErrorDialog(activity); return; - case DvrStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT: + case RecordingStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT: showDvrNoFreeSpaceErrorDialog(activity, recordingRequestRunnable); return; + default: // fall out } } recordingRequestRunnable.run(); } - /** - * Shows the schedule dialog. - */ - public static void showScheduleDialog(Activity activity, Program program, - boolean addCurrentProgramToSeries) { + /** Shows the schedule dialog. */ + public static void showScheduleDialog( + Activity activity, Program program, boolean addCurrentProgramToSeries) { if (SoftPreconditions.checkNotNull(program) == null) { return; } Bundle args = new Bundle(); args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); - args.putBoolean(DvrScheduleFragment.KEY_ADD_CURRENT_PROGRAM_TO_SERIES, - addCurrentProgramToSeries); + args.putBoolean( + DvrScheduleFragment.KEY_ADD_CURRENT_PROGRAM_TO_SERIES, addCurrentProgramToSeries); showDialogFragment(activity, new DvrScheduleDialogFragment(), args, true, true); } - /** - * Shows the recording duration options dialog. - */ + /** Shows the recording duration options dialog. */ public static void showChannelRecordDurationOptions(Activity activity, Channel channel) { if (SoftPreconditions.checkNotNull(channel) == null) { return; @@ -139,9 +137,7 @@ public class DvrUiHelper { showDialogFragment(activity, new DvrChannelRecordDurationOptionDialogFragment(), args); } - /** - * Shows the dialog which says that the new schedule conflicts with others. - */ + /** Shows the dialog which says that the new schedule conflicts with others. */ public static void showScheduleConflictDialog(Activity activity, Program program) { if (program == null) { return; @@ -151,9 +147,7 @@ public class DvrUiHelper { showDialogFragment(activity, new DvrProgramConflictDialogFragment(), args, false, true); } - /** - * Shows the conflict dialog for the channel watching. - */ + /** Shows the conflict dialog for the channel watching. */ public static void showChannelWatchConflictDialog(MainActivity activity, Channel channel) { if (channel == null) { return; @@ -163,63 +157,60 @@ public class DvrUiHelper { showDialogFragment(activity, new DvrChannelWatchConflictDialogFragment(), args); } - /** - * Shows DVR insufficient space error dialog. - */ - public static void showDvrInsufficientSpaceErrorDialog(MainActivity activity, - Set<String> failedScheduledRecordingInfoSet) { + /** Shows DVR insufficient space error dialog. */ + public static void showDvrInsufficientSpaceErrorDialog( + MainActivity activity, Set<String> failedScheduledRecordingInfoSet) { Bundle args = new Bundle(); ArrayList<String> failedScheduledRecordingInfoArray = new ArrayList<>(failedScheduledRecordingInfoSet); - args.putStringArrayList(DvrInsufficientSpaceErrorFragment.FAILED_SCHEDULED_RECORDING_INFOS, + args.putStringArrayList( + DvrInsufficientSpaceErrorFragment.FAILED_SCHEDULED_RECORDING_INFOS, failedScheduledRecordingInfoArray); showDialogFragment(activity, new DvrInsufficientSpaceErrorDialogFragment(), args); - Utils.clearRecordingFailedReason(activity, - TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); + Utils.clearRecordingFailedReason( + activity, TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); Utils.clearFailedScheduledRecordingInfoSet(activity); } /** * Shows DVR no free space error dialog. * - * @param recordingRequestRunnable the recording request to be executed when users choose - * {@link DvrGuidedStepFragment#ACTION_RECORD_ANYWAY}. + * @param recordingRequestRunnable the recording request to be executed when users choose {@link + * DvrGuidedStepFragment#ACTION_RECORD_ANYWAY}. */ - public static void showDvrNoFreeSpaceErrorDialog(Activity activity, - Runnable recordingRequestRunnable) { + public static void showDvrNoFreeSpaceErrorDialog( + Activity activity, Runnable recordingRequestRunnable) { DvrHalfSizedDialogFragment fragment = new DvrNoFreeSpaceErrorDialogFragment(); - fragment.setOnActionClickListener(new HalfSizedDialogFragment.OnActionClickListener() { - @Override - public void onActionClick(long actionId) { - if (actionId == DvrGuidedStepFragment.ACTION_RECORD_ANYWAY) { - recordingRequestRunnable.run(); - } else if (actionId == DvrGuidedStepFragment.ACTION_DELETE_RECORDINGS) { - Intent intent = new Intent(activity, DvrBrowseActivity.class); - activity.startActivity(intent); - } - } - }); + fragment.setOnActionClickListener( + new HalfSizedDialogFragment.OnActionClickListener() { + @Override + public void onActionClick(long actionId) { + if (actionId == DvrGuidedStepFragment.ACTION_RECORD_ANYWAY) { + recordingRequestRunnable.run(); + } else if (actionId == DvrGuidedStepFragment.ACTION_DELETE_RECORDINGS) { + Intent intent = new Intent(activity, DvrBrowseActivity.class); + activity.startActivity(intent); + } + } + }); showDialogFragment(activity, fragment, null); } - /** - * Shows DVR missing storage error dialog. - */ + /** Shows DVR missing storage error dialog. */ private static void showDvrMissingStorageErrorDialog(Activity activity) { showDialogFragment(activity, new DvrMissingStorageErrorDialogFragment(), null); } - /** - * Shows DVR small sized storage error dialog. - */ + /** Shows DVR small sized storage error dialog. */ public static void showDvrSmallSizedStorageErrorDialog(Activity activity) { showDialogFragment(activity, new DvrSmallSizedStorageErrorDialogFragment(), null); } - /** - * Shows stop recording dialog. - */ - public static void showStopRecordingDialog(Activity activity, long channelId, int reason, + /** Shows stop recording dialog. */ + public static void showStopRecordingDialog( + Activity activity, + long channelId, + int reason, HalfSizedDialogFragment.OnActionClickListener listener) { Bundle args = new Bundle(); args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channelId); @@ -229,9 +220,7 @@ public class DvrUiHelper { showDialogFragment(activity, fragment, args); } - /** - * Shows "already scheduled" dialog. - */ + /** Shows "already scheduled" dialog. */ public static void showAlreadyScheduleDialog(Activity activity, Program program) { if (program == null) { return; @@ -241,9 +230,7 @@ public class DvrUiHelper { showDialogFragment(activity, new DvrAlreadyScheduledDialogFragment(), args, false, true); } - /** - * Shows "already recorded" dialog. - */ + /** Shows "already recorded" dialog. */ public static void showAlreadyRecordedDialog(Activity activity, Program program) { if (program == null) { return; @@ -253,37 +240,49 @@ public class DvrUiHelper { showDialogFragment(activity, new DvrAlreadyRecordedDialogFragment(), args, false, true); } + /** Shows program information dialog. */ + public static void showProgramInfoDialog(Activity activity, Program program) { + if (program == null || !BuildConfig.ENG) { + return; + } + Bundle args = new Bundle(); + args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); + showDialogFragment(activity, new DvrFutureProgramInfoDialogFragment(), args, false, true); + } + /** * Handle the request of recording a current program. It will handle creating schedules and * shows the proper dialog and toast message respectively for timed-recording and program * recording cases. * - * @param addProgramToSeries denotes whether the program to be recorded should be added into - * the series recording when users choose to record the entire series. + * @param addProgramToSeries denotes whether the program to be recorded should be added into the + * series recording when users choose to record the entire series. */ - public static void requestRecordingCurrentProgram(Activity activity, - Channel channel, Program program, boolean addProgramToSeries) { + public static void requestRecordingCurrentProgram( + Activity activity, Channel channel, Program program, boolean addProgramToSeries) { if (program == null) { DvrUiHelper.showChannelRecordDurationOptions(activity, channel); } else if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) { - String msg = activity.getString(R.string.dvr_msg_current_program_scheduled, - program.getTitle(), Utils.toTimeString(program.getEndTimeUtcMillis(), false)); + String msg = + activity.getString( + R.string.dvr_msg_current_program_scheduled, + program.getTitle(), + Utils.toTimeString(program.getEndTimeUtcMillis(), false)); Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show(); } } /** - * Handle the request of recording a future program. It will handle creating schedules and - * shows the proper toast message. + * Handle the request of recording a future program. It will handle creating schedules and shows + * the proper toast message. * - * @param addProgramToSeries denotes whether the program to be recorded should be added into - * the series recording when users choose to record the entire series. + * @param addProgramToSeries denotes whether the program to be recorded should be added into the + * series recording when users choose to record the entire series. */ - public static void requestRecordingFutureProgram(Activity activity, - Program program, boolean addProgramToSeries) { + public static void requestRecordingFutureProgram( + Activity activity, Program program, boolean addProgramToSeries) { if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) { - String msg = activity.getString( - R.string.dvr_msg_program_scheduled, program.getTitle()); + String msg = activity.getString(R.string.dvr_msg_program_scheduled, program.getTitle()); ToastUtils.show(activity, msg, Toast.LENGTH_SHORT); } } @@ -292,12 +291,12 @@ public class DvrUiHelper { * Handles the action to create the new schedule. It returns {@code true} if the schedule is * added and there's no additional UI, otherwise {@code false}. */ - private static boolean handleCreateSchedule(Activity activity, Program program, - boolean addProgramToSeries) { + private static boolean handleCreateSchedule( + Activity activity, Program program, boolean addProgramToSeries) { if (program == null) { return false; } - DvrManager dvrManager = TvApplication.getSingletons(activity).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(activity).getDvrManager(); if (!program.isEpisodic()) { // One time recording. dvrManager.addSchedule(program); @@ -307,18 +306,24 @@ public class DvrUiHelper { } } else { // Show recorded program rather than the schedule. - RecordedProgram recordedProgram = dvrManager.getRecordedProgram(program.getTitle(), - program.getSeasonNumber(), program.getEpisodeNumber()); + RecordedProgram recordedProgram = + dvrManager.getRecordedProgram( + program.getTitle(), + program.getSeasonNumber(), + program.getEpisodeNumber()); if (recordedProgram != null) { DvrUiHelper.showAlreadyRecordedDialog(activity, program); return false; } - ScheduledRecording duplicate = dvrManager.getScheduledRecording(program.getTitle(), - program.getSeasonNumber(), program.getEpisodeNumber()); + ScheduledRecording duplicate = + dvrManager.getScheduledRecording( + program.getTitle(), + program.getSeasonNumber(), + program.getEpisodeNumber()); if (duplicate != null && (duplicate.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || duplicate.getState() - == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { + || duplicate.getState() + == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { DvrUiHelper.showAlreadyScheduleDialog(activity, program); return false; } @@ -334,39 +339,44 @@ public class DvrUiHelper { return true; } - private static void showDialogFragment(Activity activity, - DvrHalfSizedDialogFragment dialogFragment, Bundle args) { + private static void showDialogFragment( + Activity activity, DvrHalfSizedDialogFragment dialogFragment, Bundle args) { showDialogFragment(activity, dialogFragment, args, false, false); } - private static void showDialogFragment(Activity activity, - DvrHalfSizedDialogFragment dialogFragment, Bundle args, boolean keepSidePanelHistory, + private static void showDialogFragment( + Activity activity, + DvrHalfSizedDialogFragment dialogFragment, + Bundle args, + boolean keepSidePanelHistory, boolean keepProgramGuide) { dialogFragment.setArguments(args); if (activity instanceof MainActivity) { - ((MainActivity) activity).getOverlayManager() - .showDialogFragment(DvrHalfSizedDialogFragment.DIALOG_TAG, dialogFragment, - keepSidePanelHistory, keepProgramGuide); + ((MainActivity) activity) + .getOverlayManager() + .showDialogFragment( + DvrHalfSizedDialogFragment.DIALOG_TAG, + dialogFragment, + keepSidePanelHistory, + keepProgramGuide); } else { - dialogFragment.show(activity.getFragmentManager(), - DvrHalfSizedDialogFragment.DIALOG_TAG); + dialogFragment.show( + activity.getFragmentManager(), DvrHalfSizedDialogFragment.DIALOG_TAG); } } - /** - * Checks whether channel watch conflict dialog is open or not. - */ + /** Checks whether channel watch conflict dialog is open or not. */ public static boolean isChannelWatchConflictDialogShown(MainActivity activity) { - return activity.getOverlayManager().getCurrentDialog() instanceof - DvrChannelWatchConflictDialogFragment; + return activity.getOverlayManager().getCurrentDialog() + instanceof DvrChannelWatchConflictDialogFragment; } - private static ScheduledRecording getEarliestScheduledRecording(List<ScheduledRecording> - recordings) { + private static ScheduledRecording getEarliestScheduledRecording( + List<ScheduledRecording> recordings) { ScheduledRecording earlistScheduledRecording = null; if (!recordings.isEmpty()) { - Collections.sort(recordings, - ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); + Collections.sort( + recordings, ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); earlistScheduledRecording = recordings.get(0); } return earlistScheduledRecording; @@ -378,10 +388,10 @@ public class DvrUiHelper { * @param programId the ID of the recorded program going to be played. * @param seekTimeMs the seek position to initial playback. * @param pinChecked {@code true} if the pin code for parental controls has already been - * verified, otherwise {@code false}. + * verified, otherwise {@code false}. */ - public static void startPlaybackActivity(Context context, long programId, - long seekTimeMs, boolean pinChecked) { + public static void startPlaybackActivity( + Context context, long programId, long seekTimeMs, boolean pinChecked) { Intent intent = new Intent(context, DvrPlaybackActivity.class); intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId); if (seekTimeMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { @@ -391,51 +401,52 @@ public class DvrUiHelper { context.startActivity(intent); } - /** - * Shows the schedules activity to resolve the tune conflict. - */ + /** Shows the schedules activity to resolve the tune conflict. */ public static void startSchedulesActivityForTuneConflict(Context context, Channel channel) { if (channel == null) { return; } - List<ScheduledRecording> conflicts = TvApplication.getSingletons(context).getDvrManager() - .getConflictingSchedulesForTune(channel.getId()); + List<ScheduledRecording> conflicts = + TvSingletons.getSingletons(context) + .getDvrManager() + .getConflictingSchedulesForTune(channel.getId()); startSchedulesActivity(context, getEarliestScheduledRecording(conflicts)); } - /** - * Shows the schedules activity to resolve the one time recording conflict. - */ - public static void startSchedulesActivityForOneTimeRecordingConflict(Context context, - List<ScheduledRecording> conflicts) { + /** Shows the schedules activity to resolve the one time recording conflict. */ + public static void startSchedulesActivityForOneTimeRecordingConflict( + Context context, List<ScheduledRecording> conflicts) { startSchedulesActivity(context, getEarliestScheduledRecording(conflicts)); } - /** - * Shows the schedules activity with full schedule. - */ - public static void startSchedulesActivity(Context context, ScheduledRecording - focusedScheduledRecording) { + /** Shows the schedules activity with full schedule. */ + public static void startDvrHistoryActivity(Context context) { + Intent intent = new Intent(context, DvrHistoryActivity.class); + context.startActivity(intent); + } + + /** Shows the schedules activity with full schedule. */ + public static void startSchedulesActivity( + Context context, ScheduledRecording focusedScheduledRecording) { Intent intent = new Intent(context, DvrSchedulesActivity.class); - intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, - DvrSchedulesActivity.TYPE_FULL_SCHEDULE); + intent.putExtra( + DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity.TYPE_FULL_SCHEDULE); if (focusedScheduledRecording != null) { - intent.putExtra(DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING, + intent.putExtra( + DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING, focusedScheduledRecording); } context.startActivity(intent); } - /** - * Shows the schedules activity for series recording. - */ - public static void startSchedulesActivityForSeries(Context context, - SeriesRecording seriesRecording) { + /** Shows the schedules activity for series recording. */ + public static void startSchedulesActivityForSeries( + Context context, SeriesRecording seriesRecording) { Intent intent = new Intent(context, DvrSchedulesActivity.class); - intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, - DvrSchedulesActivity.TYPE_SERIES_SCHEDULE); - intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, - seriesRecording); + intent.putExtra( + DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity.TYPE_SERIES_SCHEDULE); + intent.putExtra( + DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, seriesRecording); context.startActivity(intent); } @@ -444,93 +455,125 @@ public class DvrUiHelper { * * @param programs list of programs which belong to the series. */ - public static void startSeriesSettingsActivity(Context context, long seriesRecordingId, - @Nullable List<Program> programs, boolean removeEmptySeriesSchedule, - boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog, + public static void startSeriesSettingsActivity( + Context context, + long seriesRecordingId, + @Nullable List<Program> programs, + boolean removeEmptySeriesSchedule, + boolean isWindowTranslucent, + boolean showViewScheduleOptionInDialog, Program currentProgram) { - SeriesRecording series = TvApplication.getSingletons(context).getDvrDataManager() - .getSeriesRecording(seriesRecordingId); + SeriesRecording series = + TvSingletons.getSingletons(context) + .getDvrDataManager() + .getSeriesRecording(seriesRecordingId); if (series == null) { return; } if (programs != null) { - startSeriesSettingsActivityInternal(context, seriesRecordingId, programs, - removeEmptySeriesSchedule, isWindowTranslucent, - showViewScheduleOptionInDialog, currentProgram); + startSeriesSettingsActivityInternal( + context, + seriesRecordingId, + programs, + removeEmptySeriesSchedule, + isWindowTranslucent, + showViewScheduleOptionInDialog, + currentProgram); } else { EpisodicProgramLoadTask episodicProgramLoadTask = new EpisodicProgramLoadTask(context, series) { - @Override - protected void onPostExecute(List<Program> loadedPrograms) { - sProgressDialog.dismiss(); - sProgressDialog = null; - startSeriesSettingsActivityInternal(context, seriesRecordingId, - loadedPrograms == null ? Collections.EMPTY_LIST : loadedPrograms, - removeEmptySeriesSchedule, isWindowTranslucent, - showViewScheduleOptionInDialog, currentProgram); - } - }.setLoadCurrentProgram(true) - .setLoadDisallowedProgram(true) - .setLoadScheduledEpisode(true) - .setIgnoreChannelOption(true); - sProgressDialog = ProgressDialog.show(context, null, context.getString( - R.string.dvr_series_progress_message_reading_programs), true, true, - new DialogInterface.OnCancelListener() { @Override - public void onCancel(DialogInterface dialogInterface) { - episodicProgramLoadTask.cancel(true); + protected void onPostExecute(List<Program> loadedPrograms) { + sProgressDialog.dismiss(); sProgressDialog = null; + startSeriesSettingsActivityInternal( + context, + seriesRecordingId, + loadedPrograms == null + ? Collections.EMPTY_LIST + : loadedPrograms, + removeEmptySeriesSchedule, + isWindowTranslucent, + showViewScheduleOptionInDialog, + currentProgram); } - }); + }.setLoadCurrentProgram(true) + .setLoadDisallowedProgram(true) + .setLoadScheduledEpisode(true) + .setIgnoreChannelOption(true); + sProgressDialog = + ProgressDialog.show( + context, + null, + context.getString( + R.string.dvr_series_progress_message_reading_programs), + true, + true, + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialogInterface) { + episodicProgramLoadTask.cancel(true); + sProgressDialog = null; + } + }); episodicProgramLoadTask.execute(); } } - private static void startSeriesSettingsActivityInternal(Context context, long seriesRecordingId, - @NonNull List<Program> programs, boolean removeEmptySeriesSchedule, - boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog, + private static void startSeriesSettingsActivityInternal( + Context context, + long seriesRecordingId, + @NonNull List<Program> programs, + boolean removeEmptySeriesSchedule, + boolean isWindowTranslucent, + boolean showViewScheduleOptionInDialog, Program currentProgram) { - SoftPreconditions.checkState(programs != null, - TAG, "Start series settings activity but programs is null"); + SoftPreconditions.checkState( + programs != null, TAG, "Start series settings activity but programs is null"); Intent intent = new Intent(context, DvrSeriesSettingsActivity.class); intent.putExtra(DvrSeriesSettingsActivity.SERIES_RECORDING_ID, seriesRecordingId); BigArguments.reset(); BigArguments.setArgument(DvrSeriesSettingsActivity.PROGRAM_LIST, programs); - intent.putExtra(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING, - removeEmptySeriesSchedule); + intent.putExtra( + DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING, removeEmptySeriesSchedule); intent.putExtra(DvrSeriesSettingsActivity.IS_WINDOW_TRANSLUCENT, isWindowTranslucent); - intent.putExtra(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG, + intent.putExtra( + DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG, showViewScheduleOptionInDialog); intent.putExtra(DvrSeriesSettingsActivity.CURRENT_PROGRAM, currentProgram); context.startActivity(intent); } - /** - * Shows "series recording scheduled" dialog activity. - */ - public static void StartSeriesScheduledDialogActivity(Context context, - SeriesRecording seriesRecording, boolean showViewScheduleOptionInDialog, + /** Shows "series recording scheduled" dialog activity. */ + public static void startSeriesScheduledDialogActivity( + Context context, + SeriesRecording seriesRecording, + boolean showViewScheduleOptionInDialog, List<Program> programs) { if (seriesRecording == null) { return; } Intent intent = new Intent(context, DvrSeriesScheduledDialogActivity.class); - intent.putExtra(DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, - seriesRecording.getId()); - intent.putExtra(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION, + intent.putExtra( + DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, seriesRecording.getId()); + intent.putExtra( + DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION, showViewScheduleOptionInDialog); BigArguments.reset(); - BigArguments.setArgument(DvrSeriesScheduledFragment.SERIES_SCHEDULED_KEY_PROGRAMS, - programs); + BigArguments.setArgument( + DvrSeriesScheduledFragment.SERIES_SCHEDULED_KEY_PROGRAMS, programs); context.startActivity(intent); } /** - * Shows the details activity for the DVR items. The type of DVR items may be - * {@link ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}. + * Shows the details activity for the DVR items. The type of DVR items may be {@link + * ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}. */ - public static void startDetailsActivity(Activity activity, Object dvrItem, - @Nullable ImageView imageView, boolean hideViewSchedule) { + public static void startDetailsActivity( + Activity activity, + Object dvrItem, + @Nullable ImageView imageView, + boolean hideViewSchedule) { if (dvrItem == null) { return; } @@ -544,6 +587,17 @@ public class DvrUiHelper { viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW; } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { viewType = DvrDetailsActivity.CURRENT_RECORDING_VIEW; + } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED + && schedule.getRecordedProgramId() != null) { + recordingId = schedule.getRecordedProgramId(); + viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW; + } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) { + viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW; + hideViewSchedule = true; + // TODO(b/72638385): pass detailed error message + intent.putExtra( + DvrDetailsActivity.EXTRA_FAILED_MESSAGE, + activity.getString(R.string.dvr_recording_failed)); } else { return; } @@ -561,89 +615,108 @@ public class DvrUiHelper { intent.putExtra(DvrDetailsActivity.HIDE_VIEW_SCHEDULE, hideViewSchedule); Bundle bundle = null; if (imageView != null) { - bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView, - DvrDetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + bundle = + ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, imageView, DvrDetailsActivity.SHARED_ELEMENT_NAME) + .toBundle(); } activity.startActivity(intent, bundle); } - /** - * Shows the cancel all dialog for series schedules list. - */ - public static void showCancelAllSeriesRecordingDialog(DvrSchedulesActivity activity, - SeriesRecording seriesRecording) { + /** Shows the cancel all dialog for series schedules list. */ + public static void showCancelAllSeriesRecordingDialog( + DvrSchedulesActivity activity, SeriesRecording seriesRecording) { DvrStopSeriesRecordingDialogFragment dvrStopSeriesRecordingDialogFragment = new DvrStopSeriesRecordingDialogFragment(); Bundle arguments = new Bundle(); - arguments.putParcelable(DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING, - seriesRecording); + arguments.putParcelable( + DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING, seriesRecording); dvrStopSeriesRecordingDialogFragment.setArguments(arguments); - dvrStopSeriesRecordingDialogFragment.show(activity.getFragmentManager(), - DvrStopSeriesRecordingDialogFragment.DIALOG_TAG); + dvrStopSeriesRecordingDialogFragment.show( + activity.getFragmentManager(), DvrStopSeriesRecordingDialogFragment.DIALOG_TAG); } - /** - * Shows the series deletion activity. - */ + /** Shows the series deletion activity. */ public static void startSeriesDeletionActivity(Context context, long seriesRecordingId) { Intent intent = new Intent(context, DvrSeriesDeletionActivity.class); intent.putExtra(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, seriesRecordingId); context.startActivity(intent); } - public static void showAddScheduleToast(Context context, - String title, long startTimeMs, long endTimeMs) { - String msg = (startTimeMs > System.currentTimeMillis()) ? - context.getString(R.string.dvr_msg_program_scheduled, title) - : context.getString(R.string.dvr_msg_current_program_scheduled, title, - Utils.toTimeString(endTimeMs, false)); + public static void showAddScheduleToast( + Context context, String title, long startTimeMs, long endTimeMs) { + String msg = + (startTimeMs > System.currentTimeMillis()) + ? context.getString(R.string.dvr_msg_program_scheduled, title) + : context.getString( + R.string.dvr_msg_current_program_scheduled, + title, + Utils.toTimeString(endTimeMs, false)); Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } - /** - * Returns the styled schedule's title with its season and episode number. - */ - public static CharSequence getStyledTitleWithEpisodeNumber(Context context, - ScheduledRecording schedule, int episodeNumberStyleResId) { - return getStyledTitleWithEpisodeNumber(context, schedule.getProgramTitle(), - schedule.getSeasonNumber(), schedule.getEpisodeNumber(), episodeNumberStyleResId); + /** Returns the styled schedule's title with its season and episode number. */ + public static CharSequence getStyledTitleWithEpisodeNumber( + Context context, ScheduledRecording schedule, int episodeNumberStyleResId) { + return getStyledTitleWithEpisodeNumber( + context, + schedule.getProgramTitle(), + schedule.getSeasonNumber(), + schedule.getEpisodeNumber(), + episodeNumberStyleResId); } - /** - * Returns the styled program's title with its season and episode number. - */ - public static CharSequence getStyledTitleWithEpisodeNumber(Context context, - BaseProgram program, int episodeNumberStyleResId) { - return getStyledTitleWithEpisodeNumber(context, program.getTitle(), - program.getSeasonNumber(), program.getEpisodeNumber(), episodeNumberStyleResId); + /** Returns the styled program's title with its season and episode number. */ + public static CharSequence getStyledTitleWithEpisodeNumber( + Context context, BaseProgram program, int episodeNumberStyleResId) { + return getStyledTitleWithEpisodeNumber( + context, + program.getTitle(), + program.getSeasonNumber(), + program.getEpisodeNumber(), + episodeNumberStyleResId); } @NonNull - public static CharSequence getStyledTitleWithEpisodeNumber(Context context, String title, - String seasonNumber, String episodeNumber, int episodeNumberStyleResId) { + public static CharSequence getStyledTitleWithEpisodeNumber( + Context context, + String title, + String seasonNumber, + String episodeNumber, + int episodeNumberStyleResId) { if (TextUtils.isEmpty(title)) { return ""; } SpannableStringBuilder builder; if (TextUtils.isEmpty(seasonNumber) || seasonNumber.equals("0")) { - builder = TextUtils.isEmpty(episodeNumber) ? new SpannableStringBuilder(title) : - new SpannableStringBuilder(Html.fromHtml( - context.getString(R.string.program_title_with_episode_number_no_season, - title, episodeNumber))); + builder = + TextUtils.isEmpty(episodeNumber) + ? new SpannableStringBuilder(title) + : new SpannableStringBuilder(Html.fromHtml(context.getString( + R.string.program_title_with_episode_number_no_season, + title, + episodeNumber))); } else { - builder = new SpannableStringBuilder(Html.fromHtml( - context.getString(R.string.program_title_with_episode_number, - title, seasonNumber, episodeNumber))); + builder = + new SpannableStringBuilder( + Html.fromHtml( + context.getString( + R.string.program_title_with_episode_number, + title, + seasonNumber, + episodeNumber))); } Object[] spans = builder.getSpans(0, builder.length(), Object.class); if (spans.length > 0) { if (episodeNumberStyleResId != 0) { - builder.setSpan(new TextAppearanceSpan(context, episodeNumberStyleResId), - builder.getSpanStart(spans[0]), builder.getSpanEnd(spans[0]), + builder.setSpan( + new TextAppearanceSpan(context, episodeNumberStyleResId), + builder.getSpanStart(spans[0]), + builder.getSpanEnd(spans[0]), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } builder.removeSpan(spans[0]); } return new SpannableString(builder); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/FadeBackground.java b/src/com/android/tv/dvr/ui/FadeBackground.java index 4f06ebcf..373daaaf 100644 --- a/src/com/android/tv/dvr/ui/FadeBackground.java +++ b/src/com/android/tv/dvr/ui/FadeBackground.java @@ -28,12 +28,9 @@ import android.transition.TransitionValues; import android.transition.Visibility; import android.util.AttributeSet; import android.view.ViewGroup; - import com.android.tv.R; -/** - * This transition fades in/out of the background of the view by changing the background color. - */ +/** This transition fades in/out of the background of the view by changing the background color. */ public class FadeBackground extends Transition { private final int mMode; @@ -45,22 +42,22 @@ public class FadeBackground extends Transition { } @Override - public void captureStartValues(TransitionValues transitionValues) { } + public void captureStartValues(TransitionValues transitionValues) {} @Override - public void captureEndValues(TransitionValues transitionValues) { } + public void captureEndValues(TransitionValues transitionValues) {} @Override - public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, - TransitionValues endValues) { + public Animator createAnimator( + ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { if (startValues == null || endValues == null) { return null; } Drawable background = endValues.view.getBackground(); if (background instanceof ColorDrawable) { int color = ((ColorDrawable) background).getColor(); - int transparentColor = Color.argb(0, Color.red(color), Color.green(color), - Color.blue(color)); + int transparentColor = + Color.argb(0, Color.red(color), Color.green(color), Color.blue(color)); return mMode == Visibility.MODE_OUT ? ObjectAnimator.ofArgb(background, "color", transparentColor) : ObjectAnimator.ofArgb(background, "color", transparentColor, color); diff --git a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java index 8c0af9ed..1eb8080a 100644 --- a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java +++ b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java @@ -19,9 +19,7 @@ package com.android.tv.dvr.ui; import android.support.annotation.VisibleForTesting; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.PresenterSelector; - import com.android.tv.common.SoftPreconditions; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -45,8 +43,8 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { this(presenterSelector, comparator, Integer.MAX_VALUE); } - public SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator, - int maxItemCount) { + public SortedArrayAdapter( + PresenterSelector presenterSelector, Comparator<T> comparator, int maxItemCount) { super(presenterSelector); mComparator = comparator; mMaxItemCount = maxItemCount; @@ -88,9 +86,8 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { * Adds an item in sorted order to the adapter. * * @param item The item to add in sorted order to the adapter. - * @param insertToEnd If items are inserted in a more or less sorted fashion, - * sets this parameter to {@code true} to search insertion position from - * the end to save search time. + * @param insertToEnd If items are inserted in a more or less sorted fashion, sets this + * parameter to {@code true} to search insertion position from the end to save search time. */ public final void add(T item, boolean insertToEnd) { long newItemId = getId(item); @@ -127,9 +124,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { return removeWithId((T) item); } - /** - * Removes an item which has the same ID as {@code item}. - */ + /** Removes an item which has the same ID as {@code item}. */ public boolean removeWithId(T item) { int index = indexWithId(item); return index >= 0 && index < size() && removeItems(index, 1) == 1; @@ -166,6 +161,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { /** * Changes an item in the list. + * * @param item The item to change. */ public final void change(T item) { @@ -181,9 +177,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { add(item); } - /** - * Checks whether the item is in the list. - */ + /** Checks whether the item is in the list. */ public final boolean contains(T item) { return indexWithId(item) != -1; } @@ -194,10 +188,10 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { } /** - * Returns the id of the the given {@code item}, which will be used in {@link #change} to - * decide if the given item is already existed in the adapter. + * Returns the id of the the given {@code item}, which will be used in {@link #change} to decide + * if the given item is already existed in the adapter. * - * The id must be stable. + * <p>The id must be stable. */ protected abstract long getId(T item); @@ -212,11 +206,9 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { return -1; } - /** - * Finds the position that the given item should be inserted to keep the sorted order. - */ + /** Finds the position that the given item should be inserted to keep the sorted order. */ public int findInsertPosition(T item) { - for (int i = size() - mExtraItemCount - 1; i >=0; i--) { + for (int i = size() - mExtraItemCount - 1; i >= 0; i--) { T r = (T) get(i); if (mComparator.compare(r, item) <= 0) { return i + 1; @@ -242,4 +234,4 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { } return lb; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java b/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java index 5fe9c478..0172f76f 100644 --- a/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java +++ b/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java @@ -19,8 +19,7 @@ package com.android.tv.dvr.ui; import android.content.Context; import android.support.v17.leanback.app.GuidedStepFragment; import android.support.v17.leanback.widget.GuidedAction; - -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.analytics.Tracker; /** A {@link GuidedStepFragment} with {@link Tracker} for analytics. */ @@ -30,7 +29,7 @@ public abstract class TrackedGuidedStepFragment extends GuidedStepFragment { @Override public void onAttach(Context context) { super.onAttach(context); - mTracker = TvApplication.getSingletons(context).getAnalytics().getDefaultTracker(); + mTracker = TvSingletons.getSingletons(context).getAnalytics().getDefaultTracker(); } @Override diff --git a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java index 38a78f5d..f3a6fea4 100644 --- a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java +++ b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java @@ -32,8 +32,8 @@ import android.widget.Button; class ActionPresenterSelector extends PresenterSelector { private final Presenter mOneLineActionPresenter = new OneLineActionPresenter(); private final Presenter mTwoLineActionPresenter = new TwoLineActionPresenter(); - private final Presenter[] mPresenters = new Presenter[] { - mOneLineActionPresenter, mTwoLineActionPresenter}; + private final Presenter[] mPresenters = + new Presenter[] {mOneLineActionPresenter, mTwoLineActionPresenter}; @Override public Presenter getPresenter(Object item) { @@ -65,8 +65,9 @@ class ActionPresenterSelector extends PresenterSelector { class OneLineActionPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.lb_action_1_line, parent, false); + View v = + LayoutInflater.from(parent.getContext()) + .inflate(R.layout.lb_action_1_line, parent, false); return new ActionViewHolder(v, parent.getLayoutDirection()); } @@ -87,8 +88,9 @@ class ActionPresenterSelector extends PresenterSelector { class TwoLineActionPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.lb_action_2_lines, parent, false); + View v = + LayoutInflater.from(parent.getContext()) + .inflate(R.layout.lb_action_2_lines, parent, false); return new ActionViewHolder(v, parent.getLayoutDirection()); } @@ -100,14 +102,20 @@ class ActionPresenterSelector extends PresenterSelector { vh.mAction = action; if (icon != null) { - final int startPadding = vh.view.getResources() - .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_start); - final int endPadding = vh.view.getResources() - .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_end); + final int startPadding = + vh.view + .getResources() + .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_start); + final int endPadding = + vh.view + .getResources() + .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_end); vh.view.setPaddingRelative(startPadding, 0, endPadding, 0); } else { - final int padding = vh.view.getResources() - .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal); + final int padding = + vh.view + .getResources() + .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal); vh.view.setPaddingRelative(padding, 0, padding, 0); } vh.mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null); @@ -131,4 +139,4 @@ class ActionPresenterSelector extends PresenterSelector { vh.mAction = null; } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java index bf18ddc0..7e7e1f75 100644 --- a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java @@ -21,9 +21,8 @@ import android.content.res.Resources; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; @@ -31,9 +30,7 @@ import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.DvrStopRecordingFragment; import com.android.tv.dvr.ui.DvrUiHelper; -/** - * {@link RecordingDetailsFragment} for current recording in DVR. - */ +/** {@link RecordingDetailsFragment} for current recording in DVR. */ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { private static final int ACTION_STOP_RECORDING = 1; @@ -41,7 +38,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener = new DvrDataManager.ScheduledRecordingListener() { @Override - public void onScheduledRecordingAdded(ScheduledRecording... schedules) { } + public void onScheduledRecordingAdded(ScheduledRecording... schedules) {} @Override public void onScheduledRecordingRemoved(ScheduledRecording... schedules) { @@ -58,7 +55,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { for (ScheduledRecording schedule : schedules) { if (schedule.getId() == getRecording().getId() && schedule.getState() - != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { + != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { getActivity().finish(); return; } @@ -69,7 +66,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { @Override public void onAttach(Context context) { super.onAttach(context); - mDvrDataManger = TvApplication.getSingletons(context).getDvrDataManager(); + mDvrDataManger = TvSingletons.getSingletons(context).getDvrDataManager(); mDvrDataManger.addScheduledRecordingListener(mScheduledRecordingListener); } @@ -78,9 +75,13 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(new ActionPresenterSelector()); Resources res = getResources(); - adapter.set(ACTION_STOP_RECORDING, new Action(ACTION_STOP_RECORDING, - res.getString(R.string.dvr_detail_stop_recording), null, - res.getDrawable(R.drawable.lb_ic_stop))); + adapter.set( + ACTION_STOP_RECORDING, + new Action( + ACTION_STOP_RECORDING, + res.getString(R.string.dvr_detail_stop_recording), + null, + res.getDrawable(R.drawable.lb_ic_stop))); return adapter; } @@ -90,7 +91,8 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { @Override public void onActionClicked(Action action) { if (action.getId() == ACTION_STOP_RECORDING) { - DvrUiHelper.showStopRecordingDialog(getActivity(), + DvrUiHelper.showStopRecordingDialog( + getActivity(), getRecording().getChannelId(), DvrStopRecordingFragment.REASON_USER_STOP, new HalfSizedDialogFragment.OnActionClickListener() { @@ -98,7 +100,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { public void onActionClick(long actionId) { if (actionId == DvrStopRecordingFragment.ACTION_STOP) { DvrManager dvrManager = - TvApplication.getSingletons(getContext()) + TvSingletons.getSingletons(getContext()) .getDvrManager(); dvrManager.stopRecording(getRecording()); getActivity().finish(); diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContent.java b/src/com/android/tv/dvr/ui/browse/DetailsContent.java index c1fa05d7..cba6293b 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsContent.java +++ b/src/com/android/tv/dvr/ui/browse/DetailsContent.java @@ -20,18 +20,15 @@ import android.content.Context; import android.media.tv.TvContract; import android.support.annotation.Nullable; import android.text.TextUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.data.Channel; +import com.android.tv.TvSingletons; +import com.android.tv.data.api.Channel; 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.DvrUiHelper; -/** - * A class for details content. - */ +/** A class for details content. */ class DetailsContent { /** Constant for invalid time. */ public static final long INVALID_TIME = -1; @@ -44,8 +41,8 @@ class DetailsContent { private String mBackgroundImageUri; private boolean mUsingChannelLogo; - static DetailsContent createFromRecordedProgram(Context context, - RecordedProgram recordedProgram) { + static DetailsContent createFromRecordedProgram( + Context context, RecordedProgram recordedProgram) { return new DetailsContent.Builder() .setChannelId(recordedProgram.getChannelId()) .setProgramTitle(recordedProgram.getTitle()) @@ -53,32 +50,72 @@ class DetailsContent { .setEpisodeNumber(recordedProgram.getEpisodeNumber()) .setStartTimeUtcMillis(recordedProgram.getStartTimeUtcMillis()) .setEndTimeUtcMillis(recordedProgram.getEndTimeUtcMillis()) - .setDescription(TextUtils.isEmpty(recordedProgram.getLongDescription()) - ? recordedProgram.getDescription() : recordedProgram.getLongDescription()) + .setDescription( + TextUtils.isEmpty(recordedProgram.getLongDescription()) + ? recordedProgram.getDescription() + : recordedProgram.getLongDescription()) .setPosterArtUri(recordedProgram.getPosterArtUri()) .setThumbnailUri(recordedProgram.getThumbnailUri()) .build(context); } - static DetailsContent createFromSeriesRecording(Context context, - SeriesRecording seriesRecording) { + static DetailsContent createFromSeriesRecording( + Context context, SeriesRecording seriesRecording) { return new DetailsContent.Builder() .setChannelId(seriesRecording.getChannelId()) .setTitle(seriesRecording.getTitle()) - .setDescription(TextUtils.isEmpty(seriesRecording.getLongDescription()) - ? seriesRecording.getDescription() : seriesRecording.getLongDescription()) + .setDescription( + TextUtils.isEmpty(seriesRecording.getLongDescription()) + ? seriesRecording.getDescription() + : seriesRecording.getLongDescription()) .setPosterArtUri(seriesRecording.getPosterUri()) .setThumbnailUri(seriesRecording.getPhotoUri()) .build(context); } - static DetailsContent createFromScheduledRecording(Context context, - ScheduledRecording scheduledRecording) { - Channel channel = TvApplication.getSingletons(context).getChannelDataManager() - .getChannel(scheduledRecording.getChannelId()); - String description = !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) ? - scheduledRecording.getProgramDescription() - : scheduledRecording.getProgramLongDescription(); + static DetailsContent createFromScheduledRecording( + Context context, ScheduledRecording scheduledRecording) { + Channel channel = + TvSingletons.getSingletons(context) + .getChannelDataManager() + .getChannel(scheduledRecording.getChannelId()); + String description = + !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) + ? scheduledRecording.getProgramDescription() + : scheduledRecording.getProgramLongDescription(); + if (TextUtils.isEmpty(description)) { + description = channel != null ? channel.getDescription() : null; + } + return new DetailsContent.Builder() + .setChannelId(scheduledRecording.getChannelId()) + .setProgramTitle(scheduledRecording.getProgramTitle()) + .setSeasonNumber(scheduledRecording.getSeasonNumber()) + .setEpisodeNumber(scheduledRecording.getEpisodeNumber()) + .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs()) + .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs()) + .setDescription(description) + .setPosterArtUri(scheduledRecording.getProgramPosterArtUri()) + .setThumbnailUri(scheduledRecording.getProgramThumbnailUri()) + .build(context); + } + + static DetailsContent createFromFailedScheduledRecording( + Context context, ScheduledRecording scheduledRecording, String errMsg) { + Channel channel = + TvSingletons.getSingletons(context) + .getChannelDataManager() + .getChannel(scheduledRecording.getChannelId()); + String description; + if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED + && errMsg != null) { + description = errMsg + + " (Error code: " + scheduledRecording.getFailedReason() + ")"; + } else { + description = + !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) + ? scheduledRecording.getProgramDescription() + : scheduledRecording.getProgramLongDescription(); + } if (TextUtils.isEmpty(description)) { description = channel != null ? channel.getDescription() : null; } @@ -95,60 +132,44 @@ class DetailsContent { .build(context); } - private DetailsContent() { } + private DetailsContent() {} - /** - * Returns title. - */ + /** Returns title. */ public CharSequence getTitle() { return mTitle; } - /** - * Returns start time. - */ + /** Returns start time. */ public long getStartTimeUtcMillis() { return mStartTimeUtcMillis; } - /** - * Returns end time. - */ + /** Returns end time. */ public long getEndTimeUtcMillis() { return mEndTimeUtcMillis; } - /** - * Returns description. - */ + /** Returns description. */ public String getDescription() { return mDescription; } - /** - * Returns Logo image URI as a String. - */ + /** Returns Logo image URI as a String. */ public String getLogoImageUri() { return mLogoImageUri; } - /** - * Returns background image URI as a String. - */ + /** Returns background image URI as a String. */ public String getBackgroundImageUri() { return mBackgroundImageUri; } - /** - * Returns if image URIs are from its channels' logo. - */ + /** Returns if image URIs are from its channels' logo. */ public boolean isUsingChannelLogo() { return mUsingChannelLogo; } - /** - * Copies other details content. - */ + /** Copies other details content. */ public void copyFrom(DetailsContent other) { if (this == other) { return; @@ -162,9 +183,7 @@ class DetailsContent { mUsingChannelLogo = other.mUsingChannelLogo; } - /** - * A class for building details content. - */ + /** A class for building details content. */ public static final class Builder { private final DetailsContent mDetailsContent; @@ -181,49 +200,37 @@ class DetailsContent { mDetailsContent.mEndTimeUtcMillis = INVALID_TIME; } - /** - * Sets title. - */ + /** Sets title. */ public Builder setTitle(CharSequence title) { mDetailsContent.mTitle = title; return this; } - /** - * Sets start time. - */ + /** Sets start time. */ public Builder setStartTimeUtcMillis(long startTimeUtcMillis) { mDetailsContent.mStartTimeUtcMillis = startTimeUtcMillis; return this; } - /** - * Sets end time. - */ + /** Sets end time. */ public Builder setEndTimeUtcMillis(long endTimeUtcMillis) { mDetailsContent.mEndTimeUtcMillis = endTimeUtcMillis; return this; } - /** - * Sets description. - */ + /** Sets description. */ public Builder setDescription(String description) { mDetailsContent.mDescription = description; return this; } - /** - * Sets logo image URI as a String. - */ + /** Sets logo image URI as a String. */ public Builder setLogoImageUri(String logoImageUri) { mDetailsContent.mLogoImageUri = logoImageUri; return this; } - /** - * Sets background image URI as a String. - */ + /** Sets background image URI as a String. */ public Builder setBackgroundImageUri(String backgroundImageUri) { mDetailsContent.mBackgroundImageUri = backgroundImageUri; return this; @@ -260,12 +267,18 @@ class DetailsContent { } private void createStyledTitle(Context context, Channel channel) { - CharSequence title = DvrUiHelper.getStyledTitleWithEpisodeNumber(context, - mProgramTitle, mSeasonNumber, mEpisodeNumber, - R.style.text_appearance_card_view_episode_number); + CharSequence title = + DvrUiHelper.getStyledTitleWithEpisodeNumber( + context, + mProgramTitle, + mSeasonNumber, + mEpisodeNumber, + R.style.text_appearance_card_view_episode_number); if (TextUtils.isEmpty(title)) { - mDetailsContent.mTitle = channel != null ? channel.getDisplayName() - : context.getResources().getString(R.string.no_program_information); + mDetailsContent.mTitle = + channel != null + ? channel.getDisplayName() + : context.getResources().getString(R.string.no_program_information); } else { mDetailsContent.mTitle = title; } @@ -288,20 +301,19 @@ class DetailsContent { mDetailsContent.mBackgroundImageUri = mThumbnailUri; } if (TextUtils.isEmpty(mDetailsContent.mLogoImageUri) && channel != null) { - String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId()) - .toString(); + String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId()).toString(); mDetailsContent.mLogoImageUri = channelLogoUri; mDetailsContent.mBackgroundImageUri = channelLogoUri; mDetailsContent.mUsingChannelLogo = true; } } - /** - * Builds details content. - */ + /** Builds details content. */ public DetailsContent build(Context context) { - Channel channel = TvApplication.getSingletons(context).getChannelDataManager() - .getChannel(mChannelId); + Channel channel = + TvSingletons.getSingletons(context) + .getChannelDataManager() + .getChannel(mChannelId); if (mDetailsContent.mTitle == null) { createStyledTitle(context, channel); } @@ -314,4 +326,4 @@ class DetailsContent { return detailsContent; } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java index 09b57887..aec8c411 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java @@ -16,11 +16,11 @@ package com.android.tv.dvr.ui.browse; -import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; +import android.app.Activity; import android.content.Context; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; @@ -33,24 +33,20 @@ import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityManager; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.tv.R; import com.android.tv.ui.ViewUtils; import com.android.tv.util.Utils; /** - * An {@link Presenter} for rendering a detailed description of an DVR item. - * Typically this Presenter will be used in a - * {@link android.support.v17.leanback.widget.DetailsOverviewRowPresenter}. - * Most codes of this class is originated from - * {@link android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter}. - * The latter class are re-used to provide a customized version of - * {@link android.support.v17.leanback.widget.DetailsOverviewRow}. + * An {@link Presenter} for rendering a detailed description of an DVR item. Typically this + * Presenter will be used in a {@link + * android.support.v17.leanback.widget.DetailsOverviewRowPresenter}. Most codes of this class is + * originated from {@link android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter}. + * The latter class are re-used to provide a customized version of {@link + * android.support.v17.leanback.widget.DetailsOverviewRow}. */ class DetailsContentPresenter extends Presenter { - /** - * The ViewHolder for the {@link DetailsContentPresenter}. - */ + /** The ViewHolder for the {@link DetailsContentPresenter}. */ public static class ViewHolder extends Presenter.ViewHolder { final TextView mTitle; final TextView mSubtitle; @@ -85,31 +81,40 @@ class DetailsContentPresenter extends Presenter { return false; } final int bodyLines = mBody.getLineCount(); - int maxLines = mFullTextMode ? bodyLines : - (mTitle.getLineCount() > 1 ? mBodyMinLines : mBodyMaxLines); + int maxLines = + mFullTextMode + ? bodyLines + : (mTitle.getLineCount() > 1 + ? mBodyMinLines + : mBodyMaxLines); if (bodyLines > maxLines) { mReadMoreView.setVisibility(View.VISIBLE); mDescriptionContainer.setFocusable(true); mDescriptionContainer.setClickable(true); - mDescriptionContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mFullTextMode = true; - mReadMoreView.setVisibility(View.GONE); - mDescriptionContainer.setFocusable(( - (AccessibilityManager) view.getContext() - .getSystemService( - Context.ACCESSIBILITY_SERVICE)) - .isEnabled()); - mDescriptionContainer.setClickable(false); - mDescriptionContainer.setOnClickListener(null); - int oldMaxLines = mBody.getMaxLines(); - mBody.setMaxLines(bodyLines); - // Minus 1 from line difference to eliminate the space - // originally occupied by "READ MORE" - showFullText((bodyLines - oldMaxLines - 1) * mBodyLineSpacing); - } - }); + mDescriptionContainer.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mFullTextMode = true; + mReadMoreView.setVisibility(View.GONE); + mDescriptionContainer.setFocusable( + ((AccessibilityManager) + view.getContext() + .getSystemService( + Context + .ACCESSIBILITY_SERVICE)) + .isEnabled()); + mDescriptionContainer.setClickable(false); + mDescriptionContainer.setOnClickListener(null); + int oldMaxLines = mBody.getMaxLines(); + mBody.setMaxLines(bodyLines); + // Minus 1 from line difference to eliminate the space + // originally occupied by "READ MORE" + showFullText( + (bodyLines - oldMaxLines - 1) + * mBodyLineSpacing); + } + }); } if (mReadMoreView.getVisibility() == View.VISIBLE && mSubtitle.getVisibility() == View.VISIBLE) { @@ -151,30 +156,42 @@ class DetailsContentPresenter extends Presenter { // We have to explicitly set focusable to true here for accessibility, since we might // set the view's focusable state when we need to show "READ MORE", which would remove // the default focusable state for accessibility. - mDescriptionContainer.setFocusable(((AccessibilityManager) view.getContext() - .getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled()); + mDescriptionContainer.setFocusable( + ((AccessibilityManager) + view.getContext() + .getSystemService(Context.ACCESSIBILITY_SERVICE)) + .isEnabled()); mReadMoreView = (TextView) view.findViewById(R.id.dvr_details_description_read_more); FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle); - final int titleAscent = view.getResources().getDimensionPixelSize( - R.dimen.lb_details_description_title_baseline); + final int titleAscent = + view.getResources() + .getDimensionPixelSize(R.dimen.lb_details_description_title_baseline); // Ascent is negative mTitleMargin = titleAscent + titleFontMetricsInt.ascent; - mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize( - R.dimen.lb_details_description_under_title_baseline_margin); - mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize( - R.dimen.dvr_details_description_under_subtitle_baseline_margin); + mUnderTitleBaselineMargin = + view.getResources() + .getDimensionPixelSize( + R.dimen.lb_details_description_under_title_baseline_margin); + mUnderSubtitleBaselineMargin = + view.getResources() + .getDimensionPixelSize( + R.dimen.dvr_details_description_under_subtitle_baseline_margin); - mTitleLineSpacing = view.getResources().getDimensionPixelSize( - R.dimen.lb_details_description_title_line_spacing); - mBodyLineSpacing = view.getResources().getDimensionPixelSize( - R.dimen.lb_details_description_body_line_spacing); + mTitleLineSpacing = + view.getResources() + .getDimensionPixelSize( + R.dimen.lb_details_description_title_line_spacing); + mBodyLineSpacing = + view.getResources() + .getDimensionPixelSize( + R.dimen.lb_details_description_body_line_spacing); - mBodyMaxLines = view.getResources().getInteger( - R.integer.lb_details_description_body_max_lines); - mBodyMinLines = view.getResources().getInteger( - R.integer.lb_details_description_body_min_lines); + mBodyMaxLines = + view.getResources().getInteger(R.integer.lb_details_description_body_max_lines); + mBodyMinLines = + view.getResources().getInteger(R.integer.lb_details_description_body_min_lines); mTitleMaxLines = mTitle.getMaxLines(); mTitleFontMetricsInt = getFontMetricsInt(mTitle); @@ -218,12 +235,14 @@ class DetailsContentPresenter extends Presenter { private void showFullText(int heightDiff) { final ViewGroup detailsFrame = (ViewGroup) mActivity.findViewById(R.id.details_frame); int nowHeight = ViewUtils.getLayoutHeight(detailsFrame); - Animator expandAnimator = ViewUtils.createHeightAnimator( - detailsFrame, nowHeight, nowHeight + heightDiff); + Animator expandAnimator = + ViewUtils.createHeightAnimator(detailsFrame, nowHeight, nowHeight + heightDiff); expandAnimator.setDuration(mFullTextAnimationDuration); - Animator shiftAnimator = ObjectAnimator.ofPropertyValuesHolder(detailsFrame, - PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, - 0f, -(heightDiff / 2))); + Animator shiftAnimator = + ObjectAnimator.ofPropertyValuesHolder( + detailsFrame, + PropertyValuesHolder.ofFloat( + View.TRANSLATION_Y, 0f, -(heightDiff / 2))); shiftAnimator.setDuration(mFullTextAnimationDuration); AnimatorSet fullTextAnimator = new AnimatorSet(); fullTextAnimator.playTogether(expandAnimator, shiftAnimator); @@ -237,14 +256,17 @@ class DetailsContentPresenter extends Presenter { public DetailsContentPresenter(Activity activity) { super(); mActivity = activity; - mFullTextAnimationDuration = mActivity.getResources() - .getInteger(R.integer.dvr_details_full_text_animation_duration); + mFullTextAnimationDuration = + mActivity + .getResources() + .getInteger(R.integer.dvr_details_full_text_animation_duration); } @Override public final ViewHolder onCreateViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.dvr_details_description, parent, false); + View v = + LayoutInflater.from(parent.getContext()) + .inflate(R.layout.dvr_details_description, parent, false); return new ViewHolder(v); } @@ -263,8 +285,11 @@ class DetailsContentPresenter extends Presenter { } else { vh.mTitle.setText(detailsContent.getTitle()); vh.mTitle.setVisibility(View.VISIBLE); - vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight() - + vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier()); + vh.mTitle.setLineSpacing( + vh.mTitleLineSpacing + - vh.mTitle.getLineHeight() + + vh.mTitle.getLineSpacingExtra(), + vh.mTitle.getLineSpacingMultiplier()); vh.mTitle.setMaxLines(vh.mTitleMaxLines); } setTopMargin(vh.mTitle, vh.mTitleMargin); @@ -272,13 +297,19 @@ class DetailsContentPresenter extends Presenter { boolean hasSubtitle = true; if (detailsContent.getStartTimeUtcMillis() != DetailsContent.INVALID_TIME && detailsContent.getEndTimeUtcMillis() != DetailsContent.INVALID_TIME) { - vh.mSubtitle.setText(Utils.getDurationString(viewHolder.view.getContext(), - detailsContent.getStartTimeUtcMillis(), - detailsContent.getEndTimeUtcMillis(), false)); + vh.mSubtitle.setText( + Utils.getDurationString( + viewHolder.view.getContext(), + detailsContent.getStartTimeUtcMillis(), + detailsContent.getEndTimeUtcMillis(), + false)); vh.mSubtitle.setVisibility(View.VISIBLE); if (hasTitle) { - setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin - + vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent); + setTopMargin( + vh.mSubtitle, + vh.mUnderTitleBaselineMargin + + vh.mSubtitleFontMetricsInt.ascent + - vh.mTitleFontMetricsInt.descent); } else { setTopMargin(vh.mSubtitle, 0); } @@ -292,16 +323,23 @@ class DetailsContentPresenter extends Presenter { } else { vh.mBody.setText(detailsContent.getDescription()); vh.mBody.setVisibility(View.VISIBLE); - vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight() - + vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier()); + vh.mBody.setLineSpacing( + vh.mBodyLineSpacing - vh.mBody.getLineHeight() + vh.mBody.getLineSpacingExtra(), + vh.mBody.getLineSpacingMultiplier()); if (hasSubtitle) { - setTopMargin(vh.mDescriptionContainer, vh.mUnderSubtitleBaselineMargin - + vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent - - vh.mBody.getPaddingTop()); + setTopMargin( + vh.mDescriptionContainer, + vh.mUnderSubtitleBaselineMargin + + vh.mBodyFontMetricsInt.ascent + - vh.mSubtitleFontMetricsInt.descent + - vh.mBody.getPaddingTop()); } else if (hasTitle) { - setTopMargin(vh.mDescriptionContainer, vh.mUnderTitleBaselineMargin - + vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent - - vh.mBody.getPaddingTop()); + setTopMargin( + vh.mDescriptionContainer, + vh.mUnderTitleBaselineMargin + + vh.mBodyFontMetricsInt.ascent + - vh.mTitleFontMetricsInt.descent + - vh.mBody.getPaddingTop()); } else { setTopMargin(vh.mDescriptionContainer, 0); } @@ -309,11 +347,11 @@ class DetailsContentPresenter extends Presenter { } @Override - public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { } + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {} private void setTopMargin(View view, int topMargin) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); lp.topMargin = topMargin; view.setLayoutParams(lp); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java index 82fe9ce3..849360b8 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java +++ b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java @@ -23,9 +23,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.support.v17.leanback.app.BackgroundManager; -/** - * The Background Helper. - */ +/** The Background Helper. */ class DetailsViewBackgroundHelper { // Background delay serves to avoid kicking off expensive bitmap loading // in case multiple backgrounds are set in quick succession. @@ -59,11 +57,10 @@ class DetailsViewBackgroundHelper { public DetailsViewBackgroundHelper(Activity activity) { mBackgroundManager = BackgroundManager.getInstance(activity); mBackgroundManager.attach(activity.getWindow()); + mBackgroundManager.setAutoReleaseOnStop(false); } - /** - * Sets the given image to background. - */ + /** Sets the given image to background. */ public void setBackground(Drawable background) { if (mRunnable != null) { mHandler.removeCallbacks(mRunnable); @@ -72,18 +69,14 @@ class DetailsViewBackgroundHelper { mHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS); } - /** - * Sets the background color. - */ + /** Sets the background color. */ public void setBackgroundColor(int color) { if (mBackgroundManager.isAttached()) { mBackgroundManager.setColor(color); } } - /** - * Sets the background scrim. - */ + /** Sets the background scrim. */ public void setScrim(int color) { if (mBackgroundManager.isAttached()) { mBackgroundManager.setDimLayer(new ColorDrawable(color)); diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java b/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java index 07eec107..6cc1c7a1 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java +++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java @@ -20,19 +20,16 @@ import android.app.Activity; import android.content.Intent; import android.media.tv.TvInputManager; import android.os.Bundle; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; -/** - * {@link android.app.Activity} for DVR UI. - */ +/** {@link android.app.Activity} for DVR UI. */ public class DvrBrowseActivity extends Activity { private DvrBrowseFragment mFragment; @Override public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); super.onCreate(savedInstanceState); setContentView(R.layout.dvr_main); mFragment = (DvrBrowseFragment) getFragmentManager().findFragmentById(R.id.dvr_frame); @@ -49,4 +46,4 @@ public class DvrBrowseActivity extends Activity { mFragment.showScheduledRow(); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java index cb3a5745..40b3a1f0 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java +++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java @@ -16,7 +16,9 @@ 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; @@ -30,9 +32,9 @@ import android.util.Log; import android.view.View; import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +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; @@ -52,12 +54,15 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; -/** - * {@link BrowseFragment} for DVR functions. - */ -public class DvrBrowseFragment extends BrowseFragment implements - RecordedProgramListener, ScheduledRecordingListener, SeriesRecordingListener, - OnDvrScheduleLoadFinishedListener, OnRecordedProgramLoadFinishedListener { +/** {@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; @@ -67,7 +72,7 @@ public class DvrBrowseFragment extends BrowseFragment implements private boolean mShouldShowScheduleRow; private boolean mEntranceTransitionEnded; - private RecordedProgramAdapter mRecentAdapter; + private RecentRowAdapter mRecentAdapter; private ScheduleAdapter mScheduleAdapter; private SeriesAdapter mSeriesAdapter; private RecordedProgramAdapter[] mGenreAdapters = @@ -98,82 +103,143 @@ public class DvrBrowseFragment extends BrowseFragment implements } }; - 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; + 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; + } } - } 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; + 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; + } } - } else if (rhs instanceof ScheduledRecording) { - return 1; - } else { - return 0; - } - } - }; + }; + + static final Comparator<Object> RECENT_ROW_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 + .reversed() + .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs); + } else if (rhs instanceof RecordedProgram) { + ScheduledRecording scheduled = (ScheduledRecording) lhs; + RecordedProgram recorded = (RecordedProgram) rhs; + int compare = + Long.compare( + recorded.getStartTimeUtcMillis(), + scheduled.getStartTimeMs()); + // recorded program first when the start times are the same + return compare == 0 ? 1 : compare; + } else { + return -1; + } + } else if (lhs instanceof RecordedProgram) { + if (rhs instanceof RecordedProgram) { + return RecordedProgram.START_TIME_THEN_ID_COMPARATOR + .reversed() + .compare((RecordedProgram) lhs, (RecordedProgram) rhs); + } else if (rhs instanceof ScheduledRecording) { + RecordedProgram recorded = (RecordedProgram) lhs; + ScheduledRecording scheduled = (ScheduledRecording) rhs; + int compare = + Long.compare( + scheduled.getStartTimeMs(), + recorded.getStartTimeUtcMillis()); + // recorded program first when the start times are the same + return compare == 0 ? -1 : compare; + } else { + return -1; + } + } else { + return !(rhs instanceof RecordedProgram) + && !(rhs instanceof ScheduledRecording) + ? 0 : 1; + } + } + }; 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); + @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(); - } - }; + 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(); - ApplicationSingletons singletons = TvApplication.getSingletons(context); + 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)); + 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(); @@ -195,7 +261,8 @@ public class DvrBrowseFragment extends BrowseFragment implements @Override public void onDestroyView() { - getView().getViewTreeObserver() + getView() + .getViewTreeObserver() .removeOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener); super.onDestroyView(); } @@ -263,6 +330,8 @@ public class DvrBrowseFragment extends BrowseFragment implements for (ScheduledRecording scheduleRecording : scheduledRecordings) { if (needToShowScheduledRecording(scheduleRecording)) { mScheduleAdapter.add(scheduleRecording); + } else if (scheduleRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) { + mRecentAdapter.add(scheduleRecording); } } } @@ -361,30 +430,44 @@ public class DvrBrowseFragment extends BrowseFragment implements private boolean startBrowseIfDvrInitialized() { if (mDvrDataManager.isInitialized()) { // Setup rows - mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT); + mRecentAdapter = new RecentRowAdapter(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(); + // only get not started or in progress recordings + List<ScheduledRecording> schedules = mDvrDataManager.getAvailableScheduledRecordings(); 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())) { + // only get failed recordings + for (ScheduledRecording scheduledRecording + : mDvrDataManager.getFailedScheduledRecordings()) { + onScheduledRecordingAdded(scheduledRecording); + } + 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. + // 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); + 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 @@ -398,16 +481,18 @@ public class DvrBrowseFragment extends BrowseFragment implements return false; } - private void handleRecordedProgramAdded(RecordedProgram recordedProgram, - boolean updateSeriesRecording) { + 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) { + if (latestProgram == null + || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare( + latestProgram, recordedProgram) + < 0) { mSeriesId2LatestProgram.put(seriesId, recordedProgram); if (updateSeriesRecording && seriesRecording != null) { onSeriesRecordingChanged(seriesRecording); @@ -415,8 +500,8 @@ public class DvrBrowseFragment extends BrowseFragment implements } } if (seriesRecording == null) { - for (RecordedProgramAdapter adapter - : getGenreAdapters(recordedProgram.getCanonicalGenres())) { + for (RecordedProgramAdapter adapter : + getGenreAdapters(recordedProgram.getCanonicalGenres())) { adapter.add(recordedProgram); } } @@ -436,8 +521,8 @@ public class DvrBrowseFragment extends BrowseFragment implements } } } - for (RecordedProgramAdapter adapter - : getGenreAdapters(recordedProgram.getCanonicalGenres())) { + for (RecordedProgramAdapter adapter : + getGenreAdapters(recordedProgram.getCanonicalGenres())) { adapter.remove(recordedProgram); } } @@ -449,8 +534,10 @@ public class DvrBrowseFragment extends BrowseFragment implements 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) { + if (latestProgram == null + || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare( + latestProgram, recordedProgram) + <= 0) { mSeriesId2LatestProgram.put(seriesId, recordedProgram); if (seriesRecording != null) { onSeriesRecordingChanged(seriesRecording); @@ -463,8 +550,8 @@ public class DvrBrowseFragment extends BrowseFragment implements } } if (seriesRecording == null) { - updateGenreAdapters(getGenreAdapters( - recordedProgram.getCanonicalGenres()), recordedProgram); + updateGenreAdapters( + getGenreAdapters(recordedProgram.getCanonicalGenres()), recordedProgram); } else { updateGenreAdapters(new ArrayList<>(), recordedProgram); } @@ -474,8 +561,8 @@ public class DvrBrowseFragment extends BrowseFragment implements for (SeriesRecording seriesRecording : seriesRecordings) { mSeriesAdapter.add(seriesRecording); if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { - for (RecordedProgramAdapter adapter - : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { + for (RecordedProgramAdapter adapter : + getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { adapter.add(seriesRecording); } } @@ -485,8 +572,8 @@ public class DvrBrowseFragment extends BrowseFragment implements private void handleSeriesRecordingsRemoved(List<SeriesRecording> seriesRecordings) { for (SeriesRecording seriesRecording : seriesRecordings) { mSeriesAdapter.remove(seriesRecording); - for (RecordedProgramAdapter adapter - : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { + for (RecordedProgramAdapter adapter : + getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { adapter.remove(seriesRecording); } } @@ -496,8 +583,8 @@ public class DvrBrowseFragment extends BrowseFragment implements for (SeriesRecording seriesRecording : seriesRecordings) { mSeriesAdapter.change(seriesRecording); if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { - updateGenreAdapters(getGenreAdapters( - seriesRecording.getCanonicalGenreIds()), seriesRecording); + updateGenreAdapters( + getGenreAdapters(seriesRecording.getCanonicalGenreIds()), seriesRecording); } else { // Remove series recording from all genre rows if it has no recorded program updateGenreAdapters(new ArrayList<>(), seriesRecording); @@ -512,7 +599,7 @@ public class DvrBrowseFragment extends BrowseFragment implements } else { for (String genre : genres) { int genreId = GenreItems.getId(genre); - if(genreId >= mGenreAdapters.length) { + if (genreId >= mGenreAdapters.length) { Log.d(TAG, "Wrong Genre ID: " + genreId); } else { result.add(mGenreAdapters[genreId]); @@ -528,7 +615,7 @@ public class DvrBrowseFragment extends BrowseFragment implements result.add(mGenreAdapters[mGenreAdapters.length - 1]); } else { for (int genreId : genreIds) { - if(genreId >= mGenreAdapters.length) { + if (genreId >= mGenreAdapters.length) { Log.d(TAG, "Wrong Genre ID: " + genreId); } else { result.add(mGenreAdapters[genreId]); @@ -554,8 +641,9 @@ public class DvrBrowseFragment extends BrowseFragment implements } private void updateRows() { - int visibleRowsCount = 1; // Schedule's Row will never be empty - if (mRecentAdapter.isEmpty()) { + 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) { @@ -597,8 +685,9 @@ public class DvrBrowseFragment extends BrowseFragment implements RecordedProgram latestProgram = null; for (RecordedProgram program : mDvrDataManager.getRecordedPrograms(seriesRecording.getId())) { - if (latestProgram == null || RecordedProgram - .START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program) < 0) { + if (latestProgram == null + || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program) + < 0) { latestProgram = program; } } @@ -622,17 +711,19 @@ public class DvrBrowseFragment extends BrowseFragment implements 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); - } - }); + 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 @@ -662,4 +753,22 @@ public class DvrBrowseFragment extends BrowseFragment implements } } } -}
\ No newline at end of file + + private class RecentRowAdapter extends SortedArrayAdapter<Object> { + RecentRowAdapter(int maxItemCount) { + super(mPresenterSelector, RECENT_ROW_COMPARATOR, maxItemCount); + } + + @Override + public long getId(Object item) { + // We takes the inverse number for the ID of scheduled recordings to make the ID stable. + if (item instanceof ScheduledRecording) { + return -((ScheduledRecording) item).getId() - 1; + } else if (item instanceof RecordedProgram) { + return ((RecordedProgram) item).getId(); + } else { + return -1; + } + } + } +} diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java index 35d21db8..0336b319 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java +++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java @@ -19,21 +19,16 @@ package com.android.tv.dvr.ui.browse; import android.app.Activity; import android.os.Bundle; import android.support.v17.leanback.app.DetailsFragment; - import android.transition.Transition; import android.transition.Transition.TransitionListener; import android.view.View; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; import com.android.tv.dialog.PinDialogFragment; -/** - * Activity to show details view in DVR. - */ +/** Activity to show details view in DVR. */ public class DvrDetailsActivity extends Activity implements PinDialogFragment.OnPinCheckedListener { - /** - * Name of record id added to the Intent. - */ + /** Name of record id added to the Intent. */ public static final String RECORDING_ID = "record_id"; /** @@ -42,46 +37,38 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On */ public static final String HIDE_VIEW_SCHEDULE = "hide_view_schedule"; - /** - * Name of details view's type added to the intent. - */ + /** Name of details view's type added to the intent. */ public static final String DETAILS_VIEW_TYPE = "details_view_type"; - /** - * Name of shared element between activities. - */ + /** Name of shared element between activities. */ public static final String SHARED_ELEMENT_NAME = "shared_element"; - /** - * CURRENT_RECORDING_VIEW refers to Current Recordings in DVR. - */ + /** Name of error message of a failed recording */ + public static final String EXTRA_FAILED_MESSAGE = "failed_message"; + + /** CURRENT_RECORDING_VIEW refers to Current Recordings in DVR. */ public static final int CURRENT_RECORDING_VIEW = 1; - /** - * SCHEDULED_RECORDING_VIEW refers to Scheduled Recordings in DVR. - */ + /** SCHEDULED_RECORDING_VIEW refers to Scheduled Recordings in DVR. */ public static final int SCHEDULED_RECORDING_VIEW = 2; - /** - * RECORDED_PROGRAM_VIEW refers to Recorded programs in DVR. - */ + /** RECORDED_PROGRAM_VIEW refers to Recorded programs in DVR. */ public static final int RECORDED_PROGRAM_VIEW = 3; - /** - * SERIES_RECORDING_VIEW refers to series recording in DVR. - */ + /** SERIES_RECORDING_VIEW refers to series recording in DVR. */ public static final int SERIES_RECORDING_VIEW = 4; private PinDialogFragment.OnPinCheckedListener mOnPinCheckedListener; @Override public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_dvr_details); long recordId = getIntent().getLongExtra(RECORDING_ID, -1); int detailsViewType = getIntent().getIntExtra(DETAILS_VIEW_TYPE, -1); boolean hideViewSchedule = getIntent().getBooleanExtra(HIDE_VIEW_SCHEDULE, false); + String failedMsg = getIntent().getStringExtra(EXTRA_FAILED_MESSAGE); if (recordId != -1 && detailsViewType != -1 && savedInstanceState == null) { Bundle args = new Bundle(); args.putLong(RECORDING_ID, recordId); @@ -90,6 +77,7 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On detailsFragment = new CurrentRecordingDetailsFragment(); } else if (detailsViewType == SCHEDULED_RECORDING_VIEW) { args.putBoolean(HIDE_VIEW_SCHEDULE, hideViewSchedule); + args.putString(EXTRA_FAILED_MESSAGE, failedMsg); detailsFragment = new ScheduledRecordingDetailsFragment(); } else if (detailsViewType == RECORDED_PROGRAM_VIEW) { detailsFragment = new RecordedProgramDetailsFragment(); @@ -97,8 +85,10 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On detailsFragment = new SeriesRecordingDetailsFragment(); } detailsFragment.setArguments(args); - getFragmentManager().beginTransaction() - .replace(R.id.dvr_details_view_frame, detailsFragment).commit(); + getFragmentManager() + .beginTransaction() + .replace(R.id.dvr_details_view_frame, detailsFragment) + .commit(); } // This is a workaround for the focus on O device diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java index 19fb7117..8f4e4dab 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java @@ -36,21 +36,19 @@ import android.support.v17.leanback.widget.SparseArrayObjectAdapter; import android.support.v17.leanback.widget.VerticalGridView; import android.text.TextUtils; import android.widget.Toast; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.common.util.CommonUtils; import com.android.tv.data.ChannelDataManager; +import com.android.tv.data.api.Channel; import com.android.tv.dialog.PinDialogFragment; import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.parental.ParentalControlSettings; -import com.android.tv.util.ImageLoader; import com.android.tv.util.ToastUtils; -import com.android.tv.util.Utils; - +import com.android.tv.util.images.ImageLoader; import java.io.File; abstract class DvrDetailsFragment extends DetailsFragment { @@ -77,8 +75,8 @@ abstract class DvrDetailsFragment extends DetailsFragment { public void onStart() { super.onStart(); // TODO: remove the workaround of b/30401180. - VerticalGridView container = (VerticalGridView) getActivity() - .findViewById(R.id.container_list); + VerticalGridView container = + (VerticalGridView) getActivity().findViewById(R.id.container_list); // Need to manually modify offset. Please refer DetailsFragment.setVerticalGridViewLayout. container.setItemAlignmentOffset(0); container.setWindowAlignmentOffset( @@ -86,27 +84,23 @@ abstract class DvrDetailsFragment extends DetailsFragment { } private void setupAdapter() { - DetailsOverviewRowPresenter rowPresenter = new DetailsOverviewRowPresenter( - new DetailsContentPresenter(getActivity())); - rowPresenter.setBackgroundColor(getResources().getColor(R.color.common_tv_background, - null)); - rowPresenter.setSharedElementEnterTransition(getActivity(), - DvrDetailsActivity.SHARED_ELEMENT_NAME); + DetailsOverviewRowPresenter rowPresenter = + new DetailsOverviewRowPresenter(new DetailsContentPresenter(getActivity())); + rowPresenter.setBackgroundColor( + getResources().getColor(R.color.common_tv_background, null)); + rowPresenter.setSharedElementEnterTransition( + getActivity(), DvrDetailsActivity.SHARED_ELEMENT_NAME); rowPresenter.setOnActionClickedListener(onCreateOnActionClickedListener()); mRowsAdapter = new ArrayObjectAdapter(onCreatePresenterSelector(rowPresenter)); setAdapter(mRowsAdapter); } - /** - * Returns details views' rows adapter. - */ + /** Returns details views' rows adapter. */ protected ArrayObjectAdapter getRowsAdapter() { - return mRowsAdapter; + return mRowsAdapter; } - /** - * Sets details overview. - */ + /** Sets details overview. */ protected void setDetailsOverviewRow(DetailsContent detailsContent) { mDetailsOverview = new DetailsOverviewRow(detailsContent); mDetailsOverview.setActionsAdapter(onCreateActionsAdapter()); @@ -114,9 +108,7 @@ abstract class DvrDetailsFragment extends DetailsFragment { onLoadLogoAndBackgroundImages(detailsContent); } - /** - * Creates and returns presenter selector will be used by rows adaptor. - */ + /** Creates and returns presenter selector will be used by rows adaptor. */ protected PresenterSelector onCreatePresenterSelector( DetailsOverviewRowPresenter rowPresenter) { ClassPresenterSelector presenterSelector = new ClassPresenterSelector(); @@ -130,11 +122,9 @@ abstract class DvrDetailsFragment extends DetailsFragment { * do anything after calling {@link #onCreate(Bundle)}. If there's something subclasses have to * do after the super class did onCreate, it should override this method and put the codes here. */ - protected void onCreateInternal() { } + protected void onCreateInternal() {} - /** - * Updates actions of details overview. - */ + /** Updates actions of details overview. */ protected void updateActions() { mDetailsOverview.setActionsAdapter(onCreateActionsAdapter()); } @@ -142,14 +132,12 @@ abstract class DvrDetailsFragment extends DetailsFragment { /** * Loads recording details according to the arguments the fragment got. * - * @return false if cannot find valid recordings, else return true. If the return value - * is false, the detail activity and fragment will be ended. + * @return false if cannot find valid recordings, else return true. If the return value is + * false, the detail activity and fragment will be ended. */ abstract boolean onLoadRecordingDetails(Bundle args); - /** - * Creates actions users can interact with and their adaptor for this fragment. - */ + /** Creates actions users can interact with and their adaptor for this fragment. */ abstract SparseArrayObjectAdapter onCreateActionsAdapter(); /** @@ -158,66 +146,76 @@ abstract class DvrDetailsFragment extends DetailsFragment { */ abstract OnActionClickedListener onCreateOnActionClickedListener(); - /** - * Loads logo and background images for detail fragments. - */ + /** Loads logo and background images for detail fragments. */ protected void onLoadLogoAndBackgroundImages(DetailsContent detailsContent) { Drawable logoDrawable = null; Drawable backgroundDrawable = null; if (TextUtils.isEmpty(detailsContent.getLogoImageUri())) { - logoDrawable = getContext().getResources() - .getDrawable(R.drawable.dvr_default_poster, null); + logoDrawable = + getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null); mDetailsOverview.setImageDrawable(logoDrawable); } if (TextUtils.isEmpty(detailsContent.getBackgroundImageUri())) { - backgroundDrawable = getContext().getResources() - .getDrawable(R.drawable.dvr_default_poster, null); + backgroundDrawable = + getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null); mBackgroundHelper.setBackground(backgroundDrawable); } if (logoDrawable != null && backgroundDrawable != null) { return; } - if (logoDrawable == null && backgroundDrawable == null - && detailsContent.getLogoImageUri().equals( - detailsContent.getBackgroundImageUri())) { - ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(), - new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE, - getContext())); + if (logoDrawable == null + && backgroundDrawable == null + && detailsContent + .getLogoImageUri() + .equals(detailsContent.getBackgroundImageUri())) { + ImageLoader.loadBitmap( + getContext(), + detailsContent.getLogoImageUri(), + new MyImageLoaderCallback( + this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE, getContext())); return; } if (logoDrawable == null) { int imageWidth = getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_width); - int imageHeight = getResources() - .getDimensionPixelSize(R.dimen.dvr_details_poster_height); - ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(), - imageWidth, imageHeight, + int imageHeight = + getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_height); + ImageLoader.loadBitmap( + getContext(), + detailsContent.getLogoImageUri(), + imageWidth, + imageHeight, new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE, getContext())); } if (backgroundDrawable == null) { - ImageLoader.loadBitmap(getContext(), detailsContent.getBackgroundImageUri(), + ImageLoader.loadBitmap( + getContext(), + detailsContent.getBackgroundImageUri(), new MyImageLoaderCallback(this, LOAD_BACKGROUND_IMAGE, getContext())); } } protected void startPlayback(RecordedProgram recordedProgram, long seekTimeMs) { - if (Utils.isInBundledPackageSet(recordedProgram.getPackageName()) && - !isDataUriAccessible(recordedProgram.getDataUri())) { + if (CommonUtils.isInBundledPackageSet(recordedProgram.getPackageName()) + && !isDataUriAccessible(recordedProgram.getDataUri())) { // Since cleaning RecordedProgram from forgotten storage will take some time, // ignore playback until cleaning is finished. - ToastUtils.show(getContext(), + ToastUtils.show( + getContext(), getContext().getResources().getString(R.string.dvr_toast_recording_deleted), Toast.LENGTH_SHORT); return; } long programId = recordedProgram.getId(); - ParentalControlSettings parental = TvApplication.getSingletons(getActivity()) - .getTvInputManagerHelper().getParentalControlSettings(); + ParentalControlSettings parental = + TvSingletons.getSingletons(getActivity()) + .getTvInputManagerHelper() + .getParentalControlSettings(); if (!parental.isParentalControlsEnabled()) { DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false); return; } ChannelDataManager channelDataManager = - TvApplication.getSingletons(getActivity()).getChannelDataManager(); + TvSingletons.getSingletons(getActivity()).getChannelDataManager(); Channel channel = channelDataManager.getChannel(recordedProgram.getChannelId()); if (channel != null && channel.isLocked()) { checkPinToPlay(recordedProgram, seekTimeMs); @@ -249,36 +247,43 @@ abstract class DvrDetailsFragment extends DetailsFragment { private void checkPinToPlay(RecordedProgram recordedProgram, long seekTimeMs) { SoftPreconditions.checkState(getActivity() instanceof DvrDetailsActivity); if (getActivity() instanceof DvrDetailsActivity) { - ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(new OnPinCheckedListener() { - @Override - public void onPinChecked(boolean checked, int type, String rating) { - ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(null); - if (checked && type == PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM) { - DvrUiHelper.startPlaybackActivity(getContext(), recordedProgram.getId(), - seekTimeMs, true); - } - } - }); + ((DvrDetailsActivity) getActivity()) + .setOnPinCheckListener( + new OnPinCheckedListener() { + @Override + public void onPinChecked(boolean checked, int type, String rating) { + ((DvrDetailsActivity) getActivity()) + .setOnPinCheckListener(null); + if (checked + && type + == PinDialogFragment + .PIN_DIALOG_TYPE_UNLOCK_PROGRAM) { + DvrUiHelper.startPlaybackActivity( + getContext(), + recordedProgram.getId(), + seekTimeMs, + true); + } + } + }); PinDialogFragment.create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM) .show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG); } } - private static class MyImageLoaderCallback extends - ImageLoader.ImageLoaderCallback<DvrDetailsFragment> { + private static class MyImageLoaderCallback + extends ImageLoader.ImageLoaderCallback<DvrDetailsFragment> { private final Context mContext; private final int mLoadType; - public MyImageLoaderCallback(DvrDetailsFragment fragment, - int loadType, Context context) { + public MyImageLoaderCallback(DvrDetailsFragment fragment, int loadType, Context context) { super(fragment); mLoadType = loadType; mContext = context; } @Override - public void onBitmapLoaded(DvrDetailsFragment fragment, - @Nullable Bitmap bitmap) { + public void onBitmapLoaded(DvrDetailsFragment fragment, @Nullable Bitmap bitmap) { Drawable drawable; int loadType = mLoadType; if (bitmap == null) { diff --git a/src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java new file mode 100644 index 00000000..c6288ef0 --- /dev/null +++ b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 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; + +/** Special object for schedule preview; */ +final class DvrHistoryCardHolder { + /** Full schedule card holder. */ + static final DvrHistoryCardHolder DVR_HISTORY_CARD_HOLDER = new DvrHistoryCardHolder(); + + private DvrHistoryCardHolder() {} +} diff --git a/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java new file mode 100644 index 00000000..62c050c9 --- /dev/null +++ b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 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.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import com.android.tv.R; +import com.android.tv.dvr.ui.DvrUiHelper; + +/** Presents a DVR history card view in the {@link DvrBrowseFragment}. */ +class DvrHistoryCardPresenter extends DvrItemPresenter<Object> { + private final Drawable mIconDrawable; + private final String mCardTitleText; + + DvrHistoryCardPresenter(Context context) { + super(context); + mIconDrawable = mContext.getDrawable(R.drawable.dvr_full_schedule); + mCardTitleText = mContext.getString(R.string.dvr_history_card_view_title); + } + + @Override + public DvrItemViewHolder onCreateDvrItemViewHolder() { + return new DvrItemViewHolder(new RecordingCardView(mContext)); + } + + @Override + public void onBindDvrItemViewHolder(DvrItemViewHolder vh, Object o) { + final RecordingCardView cardView = (RecordingCardView) vh.view; + + cardView.setTitle(mCardTitleText); + cardView.setImage(mIconDrawable); + } + + @Override + public void onUnbindViewHolder(ViewHolder vh) { + ((RecordingCardView) vh.view).reset(); + super.onUnbindViewHolder(vh); + } + + @Override + protected View.OnClickListener onCreateOnClickListener() { + return new View.OnClickListener() { + @Override + public void onClick(View view) { + DvrUiHelper.startDvrHistoryActivity(mContext); + } + }; + } +} diff --git a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java index df0e61c1..4298d86a 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java @@ -23,18 +23,15 @@ import android.support.v17.leanback.widget.Presenter; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; - import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.ui.DvrUiHelper; - import java.util.HashSet; import java.util.Set; /** * An abstract class to present DVR items in {@link RecordingCardView}, which is mainly used in - * {@link DvrBrowseFragment}. DVR items might include: - * {@link com.android.tv.dvr.data.ScheduledRecording}, - * {@link com.android.tv.dvr.data.RecordedProgram}, and + * {@link DvrBrowseFragment}. DVR items might include: {@link + * com.android.tv.dvr.data.ScheduledRecording}, {@link com.android.tv.dvr.data.RecordedProgram}, and * {@link com.android.tv.dvr.data.SeriesRecording}. */ public abstract class DvrItemPresenter<T> extends Presenter { @@ -51,9 +48,9 @@ public abstract class DvrItemPresenter<T> extends Presenter { return (RecordingCardView) view; } - protected void onBound(T item) { } + protected void onBound(T item) {} - protected void onUnbound() { } + protected void onUnbound() {} } DvrItemPresenter(Context context) { @@ -94,9 +91,7 @@ public abstract class DvrItemPresenter<T> extends Presenter { viewHolder.view.setOnClickListener(null); } - /** - * Unbinds all bound view holders. - */ + /** Unbinds all bound view holders. */ public void unbindAllViewHolders() { // When browse fragments are destroyed, RecyclerView would not call presenters' // onUnbindViewHolder(). We should handle it by ourselves to prevent resources leaks. @@ -105,36 +100,28 @@ public abstract class DvrItemPresenter<T> extends Presenter { } } - /** - * This method will be called when a {@link DvrItemViewHolder} is needed to be created. - */ - abstract protected DvrItemViewHolder onCreateDvrItemViewHolder(); + /** This method will be called when a {@link DvrItemViewHolder} is needed to be created. */ + protected abstract DvrItemViewHolder onCreateDvrItemViewHolder(); - /** - * This method will be called when a {@link DvrItemViewHolder} is bound to a DVR item. - */ - abstract protected void onBindDvrItemViewHolder(DvrItemViewHolder viewHolder, T item); + /** This method will be called when a {@link DvrItemViewHolder} is bound to a DVR item. */ + protected abstract void onBindDvrItemViewHolder(DvrItemViewHolder viewHolder, T item); - /** - * Returns context. - */ + /** Returns context. */ protected Context getContext() { return mContext; } - /** - * Creates {@link OnClickListener} for DVR library's card views. - */ + /** Creates {@link OnClickListener} for DVR library's card views. */ protected OnClickListener onCreateOnClickListener() { return new OnClickListener() { @Override public void onClick(View view) { if (view instanceof RecordingCardView) { RecordingCardView v = (RecordingCardView) view; - DvrUiHelper.startDetailsActivity((Activity) v.getContext(), - v.getTag(), v.getImageView(), false); + DvrUiHelper.startDetailsActivity( + (Activity) v.getContext(), v.getTag(), v.getImageView(), false); } } }; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java index 37a72eaf..a2d1cb28 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java @@ -19,7 +19,6 @@ package com.android.tv.dvr.ui.browse; import android.content.Context; import android.support.v17.leanback.widget.ListRowPresenter; import android.view.ViewGroup; - import com.android.tv.R; /** A list row presenter to display expand/fold card views list. */ diff --git a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java b/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java index 311137a9..6def818f 100644 --- a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java +++ b/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java @@ -16,14 +16,10 @@ package com.android.tv.dvr.ui.browse; -/** - * Special object for schedule preview; - */ +/** Special object for schedule preview; */ final class FullScheduleCardHolder { - /** - * Full schedule card holder. - */ + /** Full schedule card holder. */ static final FullScheduleCardHolder FULL_SCHEDULE_CARD_HOLDER = new FullScheduleCardHolder(); - private FullScheduleCardHolder() { } + private FullScheduleCardHolder() {} } diff --git a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java b/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java index 94c67eec..af0f24c0 100644 --- a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java @@ -19,20 +19,15 @@ package com.android.tv.dvr.ui.browse; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.View; -import android.view.ViewGroup; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.util.Utils; - import java.util.Collections; import java.util.List; -/** - * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. - */ +/** Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */ class FullSchedulesCardPresenter extends DvrItemPresenter<Object> { private final Drawable mIconDrawable; private final String mCardTitleText; @@ -54,16 +49,26 @@ class FullSchedulesCardPresenter extends DvrItemPresenter<Object> { cardView.setTitle(mCardTitleText); cardView.setImage(mIconDrawable); - List<ScheduledRecording> scheduledRecordings = TvApplication.getSingletons(mContext) - .getDvrDataManager().getAvailableScheduledRecordings(); + List<ScheduledRecording> scheduledRecordings = + TvSingletons.getSingletons(mContext) + .getDvrDataManager() + .getAvailableScheduledRecordings(); int fullDays = 0; if (!scheduledRecordings.isEmpty()) { - fullDays = Utils.computeDateDifference(System.currentTimeMillis(), - Collections.max(scheduledRecordings, ScheduledRecording.START_TIME_COMPARATOR) - .getStartTimeMs()) + 1; + fullDays = + Utils.computeDateDifference( + System.currentTimeMillis(), + Collections.max( + scheduledRecordings, + ScheduledRecording.START_TIME_COMPARATOR) + .getStartTimeMs()) + + 1; } - cardView.setContent(mContext.getResources().getQuantityString( - R.plurals.dvr_full_schedule_card_view_content, fullDays, fullDays), null); + cardView.setContent( + mContext.getResources() + .getQuantityString( + R.plurals.dvr_full_schedule_card_view_content, fullDays, fullDays), + null); } @Override @@ -81,4 +86,4 @@ class FullSchedulesCardPresenter extends DvrItemPresenter<Object> { } }; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java index eb9cb26c..47b1a198 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java @@ -22,17 +22,14 @@ import android.os.Bundle; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrWatchedPositionManager; import com.android.tv.dvr.data.RecordedProgram; -/** - * {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR. - */ +/** {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR. */ public class RecordedProgramDetailsFragment extends DvrDetailsFragment implements DvrDataManager.RecordedProgramListener { private static final int ACTION_RESUME_PLAYING = 1; @@ -47,17 +44,17 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment @Override public void onCreate(Bundle savedInstanceState) { - mDvrDataManager = TvApplication.getSingletons(getContext()).getDvrDataManager(); + mDvrDataManager = TvSingletons.getSingletons(getContext()).getDvrDataManager(); mDvrDataManager.addRecordedProgramListener(this); super.onCreate(savedInstanceState); } @Override public void onCreateInternal() { - mDvrWatchedPositionManager = TvApplication.getSingletons(getActivity()) - .getDvrWatchedPositionManager(); - setDetailsOverviewRow(DetailsContent - .createFromRecordedProgram(getContext(), mRecordedProgram)); + mDvrWatchedPositionManager = + TvSingletons.getSingletons(getActivity()).getDvrWatchedPositionManager(); + setDetailsOverviewRow( + DetailsContent.createFromRecordedProgram(getContext(), mRecordedProgram)); } @Override @@ -95,20 +92,36 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment Resources res = getResources(); if (mDvrWatchedPositionManager.getWatchedStatus(mRecordedProgram) == DvrWatchedPositionManager.DVR_WATCHED_STATUS_WATCHING) { - adapter.set(ACTION_RESUME_PLAYING, new Action(ACTION_RESUME_PLAYING, - res.getString(R.string.dvr_detail_resume_play), null, - res.getDrawable(R.drawable.lb_ic_play))); - adapter.set(ACTION_PLAY_FROM_BEGINNING, new Action(ACTION_PLAY_FROM_BEGINNING, - res.getString(R.string.dvr_detail_play_from_beginning), null, - res.getDrawable(R.drawable.lb_ic_replay))); + adapter.set( + ACTION_RESUME_PLAYING, + new Action( + ACTION_RESUME_PLAYING, + res.getString(R.string.dvr_detail_resume_play), + null, + res.getDrawable(R.drawable.lb_ic_play))); + adapter.set( + ACTION_PLAY_FROM_BEGINNING, + new Action( + ACTION_PLAY_FROM_BEGINNING, + res.getString(R.string.dvr_detail_play_from_beginning), + null, + res.getDrawable(R.drawable.lb_ic_replay))); } else { - adapter.set(ACTION_PLAY_FROM_BEGINNING, new Action(ACTION_PLAY_FROM_BEGINNING, - res.getString(R.string.dvr_detail_watch), null, - res.getDrawable(R.drawable.lb_ic_play))); + adapter.set( + ACTION_PLAY_FROM_BEGINNING, + new Action( + ACTION_PLAY_FROM_BEGINNING, + res.getString(R.string.dvr_detail_watch), + null, + res.getDrawable(R.drawable.lb_ic_play))); } - adapter.set(ACTION_DELETE_RECORDING, new Action(ACTION_DELETE_RECORDING, - res.getString(R.string.dvr_detail_delete), null, - res.getDrawable(R.drawable.ic_delete_32dp))); + adapter.set( + ACTION_DELETE_RECORDING, + new Action( + ACTION_DELETE_RECORDING, + res.getString(R.string.dvr_detail_delete), + null, + res.getDrawable(R.drawable.ic_delete_32dp))); return adapter; } @@ -120,11 +133,13 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment if (action.getId() == ACTION_PLAY_FROM_BEGINNING) { startPlayback(mRecordedProgram, TvInputManager.TIME_SHIFT_INVALID_TIME); } else if (action.getId() == ACTION_RESUME_PLAYING) { - startPlayback(mRecordedProgram, mDvrWatchedPositionManager - .getWatchedPosition(mRecordedProgram.getId())); + startPlayback( + mRecordedProgram, + mDvrWatchedPositionManager.getWatchedPosition( + mRecordedProgram.getId())); } else if (action.getId() == ACTION_DELETE_RECORDING) { - DvrManager dvrManager = TvApplication - .getSingletons(getActivity()).getDvrManager(); + DvrManager dvrManager = + TvSingletons.getSingletons(getActivity()).getDvrManager(); dvrManager.removeRecordedProgram(mRecordedProgram); getActivity().finish(); } @@ -133,10 +148,10 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment } @Override - public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { } + public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {} @Override - public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { } + public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {} @Override public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java b/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java index 5fe162b6..e2db3ac4 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java @@ -18,17 +18,14 @@ package com.android.tv.dvr.ui.browse; import android.content.Context; import android.media.tv.TvInputManager; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrWatchedPositionManager; import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListener; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.util.Utils; -/** - * Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}. - */ +/** Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}. */ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram> { private final DvrWatchedPositionManager mDvrWatchedPositionManager; private String mTodayString; @@ -53,10 +50,16 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram> } private void setProgressBar(long watchedPositionMs) { - ((RecordingCardView) view).setProgressBar( - (watchedPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) ? null - : Math.min(100, (int) (100.0f * watchedPositionMs - / mProgram.getDurationMillis()))); + ((RecordingCardView) view) + .setProgressBar( + (watchedPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) + ? null + : Math.min( + 100, + (int) + (100.0f + * watchedPositionMs + / mProgram.getDurationMillis()))); } @Override @@ -86,15 +89,15 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram> } } - RecordedProgramPresenter(Context context, boolean showEpisodeTitle, - boolean expandTitleWhenFocused) { + RecordedProgramPresenter( + Context context, boolean showEpisodeTitle, boolean expandTitleWhenFocused) { super(context); mTodayString = mContext.getString(R.string.dvr_date_today); mYesterdayString = mContext.getString(R.string.dvr_date_yesterday); mDvrWatchedPositionManager = - TvApplication.getSingletons(mContext).getDvrWatchedPositionManager(); - mProgressBarColor = mContext.getResources() - .getColor(R.color.play_controls_progress_bar_watched); + TvSingletons.getSingletons(mContext).getDvrWatchedPositionManager(); + mProgressBarColor = + mContext.getResources().getColor(R.color.play_controls_progress_bar_watched); mShowEpisodeTitle = showEpisodeTitle; mExpandTitleWhenFocused = expandTitleWhenFocused; } @@ -114,29 +117,37 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram> final RecordedProgramViewHolder viewHolder = (RecordedProgramViewHolder) baseHolder; final RecordingCardView cardView = viewHolder.getView(); DetailsContent details = DetailsContent.createFromRecordedProgram(mContext, program); - cardView.setTitle(mShowEpisodeTitle ? - program.getEpisodeDisplayTitle(mContext) : details.getTitle()); + cardView.setTitle( + mShowEpisodeTitle ? program.getEpisodeDisplayTitle(mContext) : details.getTitle()); cardView.setImageUri(details.getLogoImageUri(), details.isUsingChannelLogo()); cardView.setContent(generateMajorContent(program), generateMinorContent(program)); cardView.setDetailBackgroundImageUri(details.getBackgroundImageUri()); } private String generateMajorContent(RecordedProgram program) { - int dateDifference = Utils.computeDateDifference(program.getStartTimeUtcMillis(), - System.currentTimeMillis()); + int dateDifference = + Utils.computeDateDifference( + program.getStartTimeUtcMillis(), System.currentTimeMillis()); if (dateDifference == 0) { return mTodayString; } else if (dateDifference == 1) { return mYesterdayString; } else { - return Utils.getDurationString(mContext, program.getStartTimeUtcMillis(), - program.getStartTimeUtcMillis(), false, true, false, 0); + return Utils.getDurationString( + mContext, + program.getStartTimeUtcMillis(), + program.getStartTimeUtcMillis(), + false, + true, + false, + 0); } } private String generateMinorContent(RecordedProgram program) { int durationMinutes = Math.max(1, Utils.getRoundOffMinsFromMs(program.getDurationMillis())); - return mContext.getResources().getQuantityString( - R.plurals.dvr_program_duration, durationMinutes, durationMinutes); + return mContext.getResources() + .getQuantityString( + R.plurals.dvr_program_duration, durationMinutes, durationMinutes); } } diff --git a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java index 767addc8..fe3c52d9 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java +++ b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java @@ -31,20 +31,19 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; - import com.android.tv.R; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.ui.ViewUtils; -import com.android.tv.util.ImageLoader; +import com.android.tv.util.images.ImageLoader; /** - * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording} - * or {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}. + * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording} or + * {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}. */ public class RecordingCardView extends BaseCardView { // This value should be the same with // android.support.v17.leanback.widget.FocusHighlightHelper.BrowseItemFocusHighlight.DURATION_MS - private final static int ANIMATION_DURATION = 150; + private static final int ANIMATION_DURATION = 150; private final ImageView mImageView; private final int mImageWidth; private final int mImageHeight; @@ -70,16 +69,19 @@ public class RecordingCardView extends BaseCardView { } public RecordingCardView(Context context, boolean expandTitleWhenFocused) { - this(context, context.getResources().getDimensionPixelSize( - R.dimen.dvr_library_card_image_layout_width), context.getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height), + this( + context, + context.getResources() + .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_width), + context.getResources() + .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height), expandTitleWhenFocused); } - public RecordingCardView(Context context, int imageWidth, int imageHeight, - boolean expandTitleWhenFocused) { + public RecordingCardView( + Context context, int imageWidth, int imageHeight, boolean expandTitleWhenFocused) { super(context); - //TODO(dvr): move these to the layout XML. + // TODO(dvr): move these to the layout XML. setCardType(BaseCardView.CARD_TYPE_INFO_UNDER_WITH_EXTRA); setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS); setFocusable(true); @@ -99,21 +101,27 @@ public class RecordingCardView extends BaseCardView { mTitleArea = (FrameLayout) findViewById(R.id.title_area); mFoldedTitleView = (TextView) findViewById(R.id.title_one_line); mExpandedTitleView = (TextView) findViewById(R.id.title_two_lines); - mFoldedTitleHeight = getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height); - mExpandedTitleHeight = getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height); + mFoldedTitleHeight = + getResources().getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height); + mExpandedTitleHeight = + getResources() + .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height); mExpandTitleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f).setDuration(ANIMATION_DURATION); - mExpandTitleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - float value = (Float) valueAnimator.getAnimatedValue(); - mExpandedTitleView.setAlpha(value); - mFoldedTitleView.setAlpha(1.0f - value); - ViewUtils.setLayoutHeight(mTitleArea, (int) (mFoldedTitleHeight - + (mExpandedTitleHeight - mFoldedTitleHeight) * value)); - } - }); + mExpandTitleAnimator.addUpdateListener( + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + float value = (Float) valueAnimator.getAnimatedValue(); + mExpandedTitleView.setAlpha(value); + mFoldedTitleView.setAlpha(1.0f - value); + ViewUtils.setLayoutHeight( + mTitleArea, + (int) + (mFoldedTitleHeight + + (mExpandedTitleHeight - mFoldedTitleHeight) + * value)); + } + }); mExpandTitleWhenFocused = expandTitleWhenFocused; } @@ -124,8 +132,12 @@ public class RecordingCardView extends BaseCardView { // loading and drawing background images during activity transitions. if (gainFocus) { if (!TextUtils.isEmpty(mDetailBackgroundImageUri)) { - ImageLoader.loadBitmap(getContext(), mDetailBackgroundImageUri, - Integer.MAX_VALUE, Integer.MAX_VALUE, null); + ImageLoader.loadBitmap( + getContext(), + mDetailBackgroundImageUri, + Integer.MAX_VALUE, + Integer.MAX_VALUE, + null); } } if (mExpandTitleWhenFocused) { @@ -186,9 +198,7 @@ public class RecordingCardView extends BaseCardView { } } - /** - * Sets progress bar. If progress is {@code null}, hides progress bar. - */ + /** Sets progress bar. If progress is {@code null}, hides progress bar. */ void setProgressBar(Integer progress) { if (progress == null) { mProgressBar.setVisibility(View.GONE); @@ -198,16 +208,14 @@ public class RecordingCardView extends BaseCardView { } } - /** - * Sets the color of progress bar. - */ + /** Sets the color of progress bar. */ void setProgressBarColor(int color) { mProgressBar.getProgressDrawable().setTint(color); } /** * Sets the image URI of the poster should be shown on the card view. - + * * @param isChannelLogo {@code true} if the image is from channels' logo. */ void setImageUri(String uri, boolean isChannelLogo) { @@ -220,14 +228,16 @@ public class RecordingCardView extends BaseCardView { if (TextUtils.isEmpty(uri)) { mImageView.setImageDrawable(mDefaultImage); } else { - ImageLoader.loadBitmap(getContext(), uri, mImageWidth, mImageHeight, + ImageLoader.loadBitmap( + getContext(), + uri, + mImageWidth, + mImageHeight, new RecordingCardImageLoaderCallback(this, uri)); } } - /** - * Sets the {@link Drawable} of the poster should be shown on the card view. - */ + /** Sets the {@link Drawable} of the poster should be shown on the card view. */ public void setImage(Drawable image) { if (image != null) { mImageView.setImageDrawable(image); @@ -255,9 +265,7 @@ public class RecordingCardView extends BaseCardView { mDetailBackgroundImageUri = uri; } - /** - * Returns image view. - */ + /** Returns image view. */ public ImageView getImageView() { return mImageView; } @@ -287,4 +295,4 @@ public class RecordingCardView extends BaseCardView { setContent(null, null); mImageView.setImageDrawable(mDefaultImage); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java index 56ec357f..aa2ccf75 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java @@ -18,34 +18,35 @@ package com.android.tv.dvr.ui.browse; import android.os.Bundle; import android.support.v17.leanback.app.DetailsFragment; - -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.data.ScheduledRecording; -/** - * {@link DetailsFragment} for recordings in DVR. - */ +/** {@link DetailsFragment} for recordings in DVR. */ abstract class RecordingDetailsFragment extends DvrDetailsFragment { private ScheduledRecording mRecording; @Override protected void onCreateInternal() { - setDetailsOverviewRow(DetailsContent - .createFromScheduledRecording(getContext(), mRecording)); + setDetailsOverviewRow( + DetailsContent.createFromScheduledRecording(getContext(), mRecording)); } @Override protected boolean onLoadRecordingDetails(Bundle args) { long scheduledRecordingId = args.getLong(DvrDetailsActivity.RECORDING_ID); - mRecording = TvApplication.getSingletons(getContext()).getDvrDataManager() - .getScheduledRecording(scheduledRecordingId); + mRecording = + TvSingletons.getSingletons(getContext()) + .getDvrDataManager() + .getScheduledRecording(scheduledRecordingId); return mRecording != null; } - /** - * Returns {@link ScheduledRecording} for the current fragment. - */ + protected ScheduledRecording getScheduledRecording() { + return mRecording; + } + + /** Returns {@link ScheduledRecording} for the current fragment. */ public ScheduledRecording getRecording() { return mRecording; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java index 958f8bf8..302b8318 100644 --- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java @@ -21,16 +21,12 @@ import android.os.Bundle; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; -import android.text.TextUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.ui.DvrUiHelper; -/** - * {@link RecordingDetailsFragment} for scheduled recording in DVR. - */ +/** {@link RecordingDetailsFragment} for scheduled recording in DVR. */ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment { private static final int ACTION_VIEW_SCHEDULE = 1; private static final int ACTION_CANCEL = 2; @@ -38,11 +34,14 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment private DvrManager mDvrManager; private Action mScheduleAction; private boolean mHideViewSchedule; + private String mFailedMessage; @Override public void onCreate(Bundle savedInstance) { - mDvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); - mHideViewSchedule = getArguments().getBoolean(DvrDetailsActivity.HIDE_VIEW_SCHEDULE); + Bundle args = getArguments(); + mDvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); + mHideViewSchedule = args.getBoolean(DvrDetailsActivity.HIDE_VIEW_SCHEDULE); + mFailedMessage = args.getString(DvrDetailsActivity.EXTRA_FAILED_MESSAGE); super.onCreate(savedInstance); } @@ -55,19 +54,37 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment } @Override + protected void onCreateInternal() { + if (mFailedMessage == null) { + super.onCreateInternal(); + return; + } + setDetailsOverviewRow( + DetailsContent.createFromFailedScheduledRecording( + getContext(), getScheduledRecording(), mFailedMessage)); + } + + @Override protected SparseArrayObjectAdapter onCreateActionsAdapter() { SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(new ActionPresenterSelector()); Resources res = getResources(); if (!mHideViewSchedule) { - mScheduleAction = new Action(ACTION_VIEW_SCHEDULE, - res.getString(R.string.dvr_detail_view_schedule), null, - res.getDrawable(getScheduleIconId())); + mScheduleAction = + new Action( + ACTION_VIEW_SCHEDULE, + res.getString(R.string.dvr_detail_view_schedule), + null, + res.getDrawable(getScheduleIconId())); adapter.set(ACTION_VIEW_SCHEDULE, mScheduleAction); } - adapter.set(ACTION_CANCEL, new Action(ACTION_CANCEL, - res.getString(R.string.dvr_detail_cancel_recording), null, - res.getDrawable(R.drawable.ic_dvr_cancel_32dp))); + adapter.set( + ACTION_CANCEL, + new Action( + ACTION_CANCEL, + res.getString(R.string.dvr_detail_cancel_recording), + null, + res.getDrawable(R.drawable.ic_dvr_cancel_32dp))); return adapter; } diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java index 273d3d19..8e028689 100644 --- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java @@ -18,18 +18,14 @@ package com.android.tv.dvr.ui.browse; import android.content.Context; import android.os.Handler; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.util.Utils; - import java.util.concurrent.TimeUnit; -/** - * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. - */ +/** Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> { private static final long PROGRESS_UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5); @@ -39,13 +35,14 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> { private final class ScheduledRecordingViewHolder extends DvrItemViewHolder { private final Handler mHandler = new Handler(); private ScheduledRecording mScheduledRecording; - private final Runnable mProgressBarUpdater = new Runnable() { - @Override - public void run() { - updateProgressBar(); - mHandler.postDelayed(this, PROGRESS_UPDATE_INTERVAL_MS); - } - }; + private final Runnable mProgressBarUpdater = + new Runnable() { + @Override + public void run() { + updateProgressBar(); + mHandler.postDelayed(this, PROGRESS_UPDATE_INTERVAL_MS); + } + }; ScheduledRecordingViewHolder(RecordingCardView view, int progressBarColor) { super(view); @@ -73,9 +70,17 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> { int recordingState = mScheduledRecording.getState(); RecordingCardView cardView = (RecordingCardView) view; if (recordingState == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { - cardView.setProgressBar(Math.max(0, Math.min((int) (100 * - (System.currentTimeMillis() - mScheduledRecording.getStartTimeMs()) - / mScheduledRecording.getDuration()), 100))); + cardView.setProgressBar( + Math.max( + 0, + Math.min( + (int) + (100 + * (System.currentTimeMillis() + - mScheduledRecording + .getStartTimeMs()) + / mScheduledRecording.getDuration()), + 100))); } else if (recordingState == ScheduledRecording.STATE_RECORDING_FINISHED) { cardView.setProgressBar(100); } else { @@ -95,9 +100,10 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> { public ScheduledRecordingPresenter(Context context) { super(context); - mDvrManager = TvApplication.getSingletons(mContext).getDvrManager(); - mProgressBarColor = mContext.getResources() - .getColor(R.color.play_controls_recording_icon_color_on_focus); + mDvrManager = TvSingletons.getSingletons(mContext).getDvrManager(); + mProgressBarColor = + mContext.getResources() + .getColor(R.color.play_controls_recording_icon_color_on_focus); } @Override @@ -106,33 +112,61 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> { } @Override - public void onBindDvrItemViewHolder(DvrItemViewHolder baseHolder, - ScheduledRecording recording) { + public void onBindDvrItemViewHolder( + DvrItemViewHolder baseHolder, ScheduledRecording recording) { final ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder; final RecordingCardView cardView = viewHolder.getView(); DetailsContent details = DetailsContent.createFromScheduledRecording(mContext, recording); cardView.setTitle(details.getTitle()); cardView.setImageUri(details.getLogoImageUri(), details.isUsingChannelLogo()); - cardView.setAffiliatedIcon(mDvrManager.isConflicting(recording) ? - R.drawable.ic_warning_white_32dp : 0); + if (mDvrManager.isConflicting(recording)) { + cardView.setAffiliatedIcon(R.drawable.ic_warning_white_32dp); + } else if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) { + cardView.setAffiliatedIcon(R.drawable.ic_error_white_48dp); + } else { + cardView.setAffiliatedIcon(0); + } cardView.setContent(generateMajorContent(recording), null); cardView.setDetailBackgroundImageUri(details.getBackgroundImageUri()); } private String generateMajorContent(ScheduledRecording recording) { - int dateDifference = Utils.computeDateDifference(System.currentTimeMillis(), - recording.getStartTimeMs()); + if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) { + return mContext.getString(R.string.dvr_recording_failed); + } + int dateDifference = + Utils.computeDateDifference(System.currentTimeMillis(), recording.getStartTimeMs()); if (dateDifference <= 0) { - return mContext.getString(R.string.dvr_date_today_time, - Utils.getDurationString(mContext, recording.getStartTimeMs(), - recording.getEndTimeMs(), false, false, true, 0)); + return mContext.getString( + R.string.dvr_date_today_time, + Utils.getDurationString( + mContext, + recording.getStartTimeMs(), + recording.getEndTimeMs(), + false, + false, + true, + 0)); } else if (dateDifference == 1) { - return mContext.getString(R.string.dvr_date_tomorrow_time, - Utils.getDurationString(mContext, recording.getStartTimeMs(), - recording.getEndTimeMs(), false, false, true, 0)); + return mContext.getString( + R.string.dvr_date_tomorrow_time, + Utils.getDurationString( + mContext, + recording.getStartTimeMs(), + recording.getEndTimeMs(), + false, + false, + true, + 0)); } else { - return Utils.getDurationString(mContext, recording.getStartTimeMs(), - recording.getStartTimeMs(), false, true, false, 0); + return Utils.getDurationString( + mContext, + recording.getStartTimeMs(), + recording.getStartTimeMs(), + false, + true, + false, + 0); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java index c2aa8e98..2cd191a7 100644 --- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java @@ -32,9 +32,8 @@ import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.PresenterSelector; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; import android.text.TextUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.BaseProgram; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrWatchedPositionManager; @@ -42,16 +41,13 @@ import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.dvr.ui.SortedArrayAdapter; - import java.util.Collections; import java.util.Comparator; import java.util.List; -/** - * {@link DetailsFragment} for series recording in DVR. - */ -public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implements - DvrDataManager.SeriesRecordingListener, DvrDataManager.RecordedProgramListener { +/** {@link DetailsFragment} for series recording in DVR. */ +public class SeriesRecordingDetailsFragment extends DvrDetailsFragment + implements DvrDataManager.SeriesRecordingListener, DvrDataManager.RecordedProgramListener { private static final int ACTION_WATCH = 1; private static final int ACTION_SERIES_SCHEDULES = 2; private static final int ACTION_DELETE = 3; @@ -77,7 +73,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement @Override public void onCreate(Bundle savedInstanceState) { - mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager(); + mDvrDataManager = TvSingletons.getSingletons(getActivity()).getDvrDataManager(); mWatchLabel = getString(R.string.dvr_detail_watch); mResumeLabel = getString(R.string.dvr_detail_series_resume); mWatchDrawable = getResources().getDrawable(R.drawable.lb_ic_play, null); @@ -87,8 +83,8 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement @Override protected void onCreateInternal() { - mDvrWatchedPositionManager = TvApplication.getSingletons(getActivity()) - .getDvrWatchedPositionManager(); + mDvrWatchedPositionManager = + TvSingletons.getSingletons(getActivity()).getDvrWatchedPositionManager(); setDetailsOverviewRow(DetailsContent.createFromSeriesRecording(getContext(), mSeries)); setupRecordedProgramsRow(); mDvrDataManager.addSeriesRecordingListener(this); @@ -119,27 +115,31 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement mActionsAdapter.clear(ACTION_WATCH); } else { String episodeStatus; - if(mDvrWatchedPositionManager.getWatchedStatus(mRecommendRecordedProgram) + if (mDvrWatchedPositionManager.getWatchedStatus(mRecommendRecordedProgram) == DvrWatchedPositionManager.DVR_WATCHED_STATUS_WATCHING) { episodeStatus = mResumeLabel; - mInitialPlaybackPositionMs = mDvrWatchedPositionManager - .getWatchedPosition(mRecommendRecordedProgram.getId()); + mInitialPlaybackPositionMs = + mDvrWatchedPositionManager.getWatchedPosition( + mRecommendRecordedProgram.getId()); } else { episodeStatus = mWatchLabel; mInitialPlaybackPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; } - String episodeDisplayNumber = mRecommendRecordedProgram.getEpisodeDisplayNumber( - getContext()); - mActionsAdapter.set(ACTION_WATCH, new Action(ACTION_WATCH, - episodeStatus, episodeDisplayNumber, mWatchDrawable)); + String episodeDisplayNumber = + mRecommendRecordedProgram.getEpisodeDisplayNumber(getContext()); + mActionsAdapter.set( + ACTION_WATCH, + new Action(ACTION_WATCH, episodeStatus, episodeDisplayNumber, mWatchDrawable)); } } @Override protected boolean onLoadRecordingDetails(Bundle args) { long recordId = args.getLong(DvrDetailsActivity.RECORDING_ID); - mSeries = TvApplication.getSingletons(getActivity()).getDvrDataManager() - .getSeriesRecording(recordId); + mSeries = + TvSingletons.getSingletons(getActivity()) + .getDvrDataManager() + .getSeriesRecording(recordId); if (mSeries == null) { return false; } @@ -162,12 +162,19 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement mActionsAdapter = new SparseArrayObjectAdapter(new ActionPresenterSelector()); Resources res = getResources(); updateWatchAction(); - mActionsAdapter.set(ACTION_SERIES_SCHEDULES, new Action(ACTION_SERIES_SCHEDULES, - getString(R.string.dvr_detail_view_schedule), null, - res.getDrawable(R.drawable.ic_schedule_32dp, null))); - mDeleteAction = new Action(ACTION_DELETE, - getString(R.string.dvr_detail_series_delete), null, - res.getDrawable(R.drawable.ic_delete_32dp, null)); + mActionsAdapter.set( + ACTION_SERIES_SCHEDULES, + new Action( + ACTION_SERIES_SCHEDULES, + getString(R.string.dvr_detail_view_schedule), + null, + res.getDrawable(R.drawable.ic_schedule_32dp, null))); + mDeleteAction = + new Action( + ACTION_DELETE, + getString(R.string.dvr_detail_series_delete), + null, + res.getDrawable(R.drawable.ic_delete_32dp, null)); if (!mRecordedPrograms.isEmpty()) { mActionsAdapter.set(ACTION_DELETE, mDeleteAction); } @@ -207,11 +214,9 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement }; } - /** - * The programs are sorted by season number and episode number. - */ + /** The programs are sorted by season number and episode number. */ private RecordedProgram getRecommendProgram(List<RecordedProgram> programs) { - for (int i = programs.size() - 1 ; i >= 0 ; i--) { + for (int i = programs.size() - 1; i >= 0; i--) { RecordedProgram program = programs.get(i); int watchedStatus = mDvrWatchedPositionManager.getWatchedStatus(program); if (watchedStatus == DvrWatchedPositionManager.DVR_WATCHED_STATUS_NEW) { @@ -230,7 +235,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement } @Override - public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { } + public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {} @Override public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { @@ -308,8 +313,10 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement for (int i = rowsAdaptor.size() - 1; i >= 0; i--) { Object row = rowsAdaptor.get(i); if (row instanceof ListRow) { - int compareResult = BaseProgram.numberCompare(seasonNumber, - ((SeasonRowAdapter) ((ListRow) row).getAdapter()).mSeasonNumber); + int compareResult = + BaseProgram.numberCompare( + seasonNumber, + ((SeasonRowAdapter) ((ListRow) row).getAdapter()).mSeasonNumber); if (compareResult == 0) { return (ListRow) row; } else if (compareResult < 0) { @@ -321,18 +328,25 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement } private ListRow createNewSeasonRow(String seasonNumber, int position) { - String seasonTitle = seasonNumber.isEmpty() ? mSeries.getTitle() - : getString(R.string.dvr_detail_series_season_title, seasonNumber); + String seasonTitle = + seasonNumber.isEmpty() + ? mSeries.getTitle() + : getString(R.string.dvr_detail_series_season_title, seasonNumber); HeaderItem header = new HeaderItem(mSeasonRowCount++, seasonTitle); ClassPresenterSelector selector = new ClassPresenterSelector(); selector.addClassPresenter(RecordedProgram.class, mRecordedProgramPresenter); - ListRow row = new ListRow(header, new SeasonRowAdapter(selector, - new Comparator<RecordedProgram>() { - @Override - public int compare(RecordedProgram lhs, RecordedProgram rhs) { - return BaseProgram.EPISODE_COMPARATOR.compare(lhs, rhs); - } - }, seasonNumber)); + ListRow row = + new ListRow( + header, + new SeasonRowAdapter( + selector, + new Comparator<RecordedProgram>() { + @Override + public int compare(RecordedProgram lhs, RecordedProgram rhs) { + return BaseProgram.EPISODE_COMPARATOR.compare(lhs, rhs); + } + }, + seasonNumber)); getRowsAdapter().add(position, row); return row; } @@ -340,7 +354,9 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement private class SeasonRowAdapter extends SortedArrayAdapter<RecordedProgram> { private String mSeasonNumber; - SeasonRowAdapter(PresenterSelector selector, Comparator<RecordedProgram> comparator, + SeasonRowAdapter( + PresenterSelector selector, + Comparator<RecordedProgram> comparator, String seasonNumber) { super(selector, comparator); mSeasonNumber = seasonNumber; @@ -351,4 +367,4 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement return program.getId(); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java b/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java index e508259d..14f9dceb 100644 --- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java +++ b/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java @@ -19,10 +19,8 @@ package com.android.tv.dvr.ui.browse; import android.content.Context; import android.media.tv.TvInputManager; import android.text.TextUtils; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrDataManager.RecordedProgramListener; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; @@ -32,27 +30,29 @@ import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListen import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; - import java.util.List; -/** - * Presents a {@link SeriesRecording} in {@link DvrBrowseFragment}. - */ +/** Presents a {@link SeriesRecording} in {@link DvrBrowseFragment}. */ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> { private final DvrDataManager mDvrDataManager; private final DvrManager mDvrManager; private final DvrWatchedPositionManager mWatchedPositionManager; - private final class SeriesRecordingViewHolder extends DvrItemViewHolder implements - WatchedPositionChangedListener, ScheduledRecordingListener, RecordedProgramListener { + private final class SeriesRecordingViewHolder extends DvrItemViewHolder + implements WatchedPositionChangedListener, + ScheduledRecordingListener, + RecordedProgramListener { private SeriesRecording mSeriesRecording; private RecordingCardView mCardView; private DvrDataManager mDvrDataManager; private DvrManager mDvrManager; private DvrWatchedPositionManager mWatchedPositionManager; - SeriesRecordingViewHolder(RecordingCardView view, DvrDataManager dvrDataManager, - DvrManager dvrManager, DvrWatchedPositionManager watchedPositionManager) { + SeriesRecordingViewHolder( + RecordingCardView view, + DvrDataManager dvrDataManager, + DvrManager dvrManager, + DvrWatchedPositionManager watchedPositionManager) { super(view); mCardView = view; mDvrDataManager = dvrDataManager; @@ -92,8 +92,8 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> { public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { boolean needToUpdateCardView = false; for (RecordedProgram recordedProgram : recordedPrograms) { - if (TextUtils.equals(recordedProgram.getSeriesId(), - mSeriesRecording.getSeriesId())) { + if (TextUtils.equals( + recordedProgram.getSeriesId(), mSeriesRecording.getSeriesId())) { mDvrDataManager.removeScheduledRecordingListener(this); mWatchedPositionManager.addListener(this, recordedProgram.getId()); needToUpdateCardView = true; @@ -108,8 +108,8 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> { public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { boolean needToUpdateCardView = false; for (RecordedProgram recordedProgram : recordedPrograms) { - if (TextUtils.equals(recordedProgram.getSeriesId(), - mSeriesRecording.getSeriesId())) { + if (TextUtils.equals( + recordedProgram.getSeriesId(), mSeriesRecording.getSeriesId())) { if (mWatchedPositionManager.getWatchedPosition(recordedProgram.getId()) == TvInputManager.TIME_SHIFT_INVALID_TIME) { mWatchedPositionManager.removeListener(this, recordedProgram.getId()); @@ -177,14 +177,15 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> { quantityStringID = R.plurals.dvr_count_new_recordings; } } - mCardView.setContent(mCardView.getResources() - .getQuantityString(quantityStringID, count, count), null); + mCardView.setContent( + mCardView.getResources().getQuantityString(quantityStringID, count, count), + null); } } public SeriesRecordingPresenter(Context context) { super(context); - ApplicationSingletons singletons = TvApplication.getSingletons(context); + TvSingletons singletons = TvSingletons.getSingletons(context); mDvrDataManager = singletons.getDvrDataManager(); mDvrManager = singletons.getDvrManager(); mWatchedPositionManager = singletons.getDvrWatchedPositionManager(); @@ -192,8 +193,11 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> { @Override public DvrItemViewHolder onCreateDvrItemViewHolder() { - return new SeriesRecordingViewHolder(new RecordingCardView(mContext), mDvrDataManager, - mDvrManager, mWatchedPositionManager); + return new SeriesRecordingViewHolder( + new RecordingCardView(mContext), + mDvrDataManager, + mDvrManager, + mWatchedPositionManager); } @Override diff --git a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java index b9407b15..77a63508 100644 --- a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2016 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 -*/ + * Copyright (C) 2016 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.list; @@ -23,23 +23,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrScheduleManager; import com.android.tv.dvr.data.ScheduledRecording; -/** - * A base fragment to show the list of schedule recordings. - */ +/** A base fragment to show the list of schedule recordings. */ public abstract class BaseDvrSchedulesFragment extends DetailsFragment implements DvrDataManager.ScheduledRecordingListener, - DvrScheduleManager.OnConflictStateChangeListener { - /** - * The key for scheduled recording which has be selected in the list. - */ + DvrScheduleManager.OnConflictStateChangeListener { + /** The key for scheduled recording which has be selected in the list. */ public static final String SCHEDULES_KEY_SCHEDULED_RECORDING = "schedules_key_scheduled_recording"; @@ -55,15 +49,15 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment mRowsAdapter = onCreateRowsAdapter(presenterSelector); setAdapter(mRowsAdapter); mRowsAdapter.start(); - ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + TvSingletons singletons = TvSingletons.getSingletons(getContext()); singletons.getDvrDataManager().addScheduledRecordingListener(this); singletons.getDvrScheduleManager().addOnConflictStateChangeListener(this); mEmptyInfoScreenView = (TextView) getActivity().findViewById(R.id.empty_info_screen); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); int firstItemPosition = getFirstItemPosition(); if (firstItemPosition != -1) { @@ -72,16 +66,12 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment return view; } - /** - * Returns rows adapter. - */ + /** Returns rows adapter. */ protected ScheduleRowAdapter getRowsAdapter() { return mRowsAdapter; } - /** - * Shows the empty message. - */ + /** Shows the empty message. */ void showEmptyMessage(int messageId) { mEmptyInfoScreenView.setText(messageId); if (mEmptyInfoScreenView.getVisibility() != View.VISIBLE) { @@ -89,9 +79,7 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment } } - /** - * Hides the empty message. - */ + /** Hides the empty message. */ void hideEmptyMessage() { if (mEmptyInfoScreenView.getVisibility() == View.VISIBLE) { mEmptyInfoScreenView.setVisibility(View.GONE); @@ -99,39 +87,32 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment } @Override - public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent, - Bundle savedInstanceState) { + public View onInflateTitleView( + LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { // Workaround of b/31046014 return null; } @Override public void onDestroy() { - ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + TvSingletons singletons = TvSingletons.getSingletons(getContext()); singletons.getDvrScheduleManager().removeOnConflictStateChangeListener(this); singletons.getDvrDataManager().removeScheduledRecordingListener(this); mRowsAdapter.stop(); super.onDestroy(); } - /** - * Creates header row presenter. - */ + /** Creates header row presenter. */ public abstract SchedulesHeaderRowPresenter onCreateHeaderRowPresenter(); - /** - * Creates rows presenter. - */ + /** Creates rows presenter. */ public abstract ScheduleRowPresenter onCreateRowPresenter(); - /** - * Creates rows adapter. - */ - public abstract ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelecor); + /** Creates rows adapter. */ + public abstract ScheduleRowAdapter onCreateRowsAdapter( + ClassPresenterSelector presenterSelector); - /** - * Gets the first focus position in schedules list. - */ + /** Gets the first focus position in schedules list. */ protected int getFirstItemPosition() { for (int i = 0; i < mRowsAdapter.size(); i++) { if (mRowsAdapter.get(i) instanceof ScheduleRow) { @@ -176,4 +157,4 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment } } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java b/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java new file mode 100644 index 00000000..623975e1 --- /dev/null +++ b/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 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.list; + +import android.app.Activity; +import android.os.Bundle; +import com.android.tv.R; +import com.android.tv.Starter; + +/** Activity to show the recording history. */ +public class DvrHistoryActivity extends Activity { + + @Override + public void onCreate(final Bundle savedInstanceState) { + Starter.start(this); + // Pass null to prevent automatically re-creating fragments + super.onCreate(null); + setContentView(R.layout.activity_dvr_history); + DvrHistoryFragment dvrHistoryFragment = new DvrHistoryFragment(); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, dvrHistoryFragment) + .commit(); + } +} diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java b/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java new file mode 100644 index 00000000..0ca05fac --- /dev/null +++ b/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 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.list; + +import android.os.Bundle; +import android.support.v17.leanback.app.DetailsFragment; +import android.support.v17.leanback.widget.ClassPresenterSelector; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.android.tv.R; +import com.android.tv.TvSingletons; +import com.android.tv.dvr.DvrDataManager; +import com.android.tv.dvr.data.RecordedProgram; +import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.DateHeaderRowPresenter; + +/** A fragment to show the DVR history. */ +public class DvrHistoryFragment extends DetailsFragment + implements DvrDataManager.ScheduledRecordingListener, + DvrDataManager.RecordedProgramListener { + + private DvrHistoryRowAdapter mRowsAdapter; + private TextView mEmptyInfoScreenView; + private DvrDataManager mDvrDataManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ClassPresenterSelector presenterSelector = new ClassPresenterSelector(); + presenterSelector.addClassPresenter( + SchedulesHeaderRow.class, new DateHeaderRowPresenter(getContext())); + presenterSelector.addClassPresenter( + ScheduleRow.class, new ScheduleRowPresenter(getContext())); + TvSingletons singletons = TvSingletons.getSingletons(getContext()); + mRowsAdapter = new DvrHistoryRowAdapter( + getContext(), presenterSelector, singletons.getClock()); + setAdapter(mRowsAdapter); + mRowsAdapter.start(); + mDvrDataManager = singletons.getDvrDataManager(); + mDvrDataManager.addScheduledRecordingListener(this); + mDvrDataManager.addRecordedProgramListener(this); + mEmptyInfoScreenView = (TextView) getActivity().findViewById(R.id.empty_info_screen); + } + + @Override + public void onDestroy() { + mDvrDataManager.removeScheduledRecordingListener(this); + mDvrDataManager.removeRecordedProgramListener(this); + super.onDestroy(); + } + + /** Shows the empty message. */ + void showEmptyMessage() { + mEmptyInfoScreenView.setText(R.string.dvr_history_empty_state); + if (mEmptyInfoScreenView.getVisibility() != View.VISIBLE) { + mEmptyInfoScreenView.setVisibility(View.VISIBLE); + } + } + + /** Hides the empty message. */ + void hideEmptyMessage() { + if (mEmptyInfoScreenView.getVisibility() == View.VISIBLE) { + mEmptyInfoScreenView.setVisibility(View.GONE); + } + } + + @Override + public View onInflateTitleView( + LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + // Workaround of b/31046014 + return null; + } + + @Override + public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { + if (mRowsAdapter != null) { + for (ScheduledRecording recording : scheduledRecordings) { + mRowsAdapter.onScheduledRecordingAdded(recording); + } + if (mRowsAdapter.size() > 0) { + hideEmptyMessage(); + } + } + } + + @Override + public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { + if (mRowsAdapter != null) { + for (ScheduledRecording recording : scheduledRecordings) { + mRowsAdapter.onScheduledRecordingRemoved(recording); + } + if (mRowsAdapter.size() == 0) { + showEmptyMessage(); + } + } + } + + @Override + public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) { + if (mRowsAdapter != null) { + for (ScheduledRecording recording : scheduledRecordings) { + mRowsAdapter.onScheduledRecordingUpdated(recording); + } + if (mRowsAdapter.size() == 0) { + showEmptyMessage(); + } else { + hideEmptyMessage(); + } + } + } + + @Override + public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { + if (mRowsAdapter != null) { + for (RecordedProgram p : recordedPrograms) { + mRowsAdapter.onScheduledRecordingAdded(p); + } + if (mRowsAdapter.size() > 0) { + hideEmptyMessage(); + } + } + + } + + @Override + public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { + if (mRowsAdapter != null) { + for (RecordedProgram program : recordedPrograms) { + mRowsAdapter.onScheduledRecordingUpdated(program); + } + if (mRowsAdapter.size() == 0) { + showEmptyMessage(); + } else { + hideEmptyMessage(); + } + } + } + + @Override + public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { + if (mRowsAdapter != null) { + for (RecordedProgram p : recordedPrograms) { + mRowsAdapter.onScheduledRecordingRemoved(p); + } + if (mRowsAdapter.size() == 0) { + showEmptyMessage(); + } + } + } +} diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java b/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java new file mode 100644 index 00000000..156d1a7e --- /dev/null +++ b/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2018 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.list; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build.VERSION_CODES; +import android.support.annotation.Nullable; +import android.support.v17.leanback.widget.ArrayObjectAdapter; +import android.support.v17.leanback.widget.ClassPresenterSelector; +import android.text.format.DateUtils; +import android.util.Log; +import com.android.tv.R; +import com.android.tv.TvSingletons; +import com.android.tv.common.SoftPreconditions; +import com.android.tv.common.util.Clock; +import com.android.tv.dvr.DvrDataManager; +import com.android.tv.dvr.data.RecordedProgram; +import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.recorder.ScheduledProgramReaper; +import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow; +import com.android.tv.util.Utils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** An adapter for DVR history. */ +@TargetApi(VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated +class DvrHistoryRowAdapter extends ArrayObjectAdapter { + private static final String TAG = "DvrHistoryRowAdapter"; + private static final boolean DEBUG = false; + + private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1); + private static final int MAX_HISTORY_DAYS = ScheduledProgramReaper.DAYS; + + private final Context mContext; + private final Clock mClock; + private final DvrDataManager mDvrDataManager; + private final List<String> mTitles = new ArrayList<>(); + private final Map<Long, ScheduledRecording> mRecordedProgramScheduleMap = new HashMap<>(); + + public DvrHistoryRowAdapter( + Context context, ClassPresenterSelector classPresenterSelector, Clock clock) { + super(classPresenterSelector); + mContext = context; + mClock = clock; + mDvrDataManager = TvSingletons.getSingletons(mContext).getDvrDataManager(); + mTitles.add(mContext.getString(R.string.dvr_date_today)); + mTitles.add(mContext.getString(R.string.dvr_date_yesterday)); + } + + /** Returns context. */ + protected Context getContext() { + return mContext; + } + + /** Starts row adapter. */ + public void start() { + clear(); + List<ScheduledRecording> recordingList = mDvrDataManager.getFailedScheduledRecordings(); + List<RecordedProgram> recordedProgramList = mDvrDataManager.getRecordedPrograms(); + + recordingList.addAll( + recordedProgramsToScheduledRecordings(recordedProgramList, MAX_HISTORY_DAYS)); + recordingList + .sort(ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.reversed()); + long deadLine = Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis()); + for (int i = 0; i < recordingList.size(); ) { + ArrayList<ScheduledRecording> section = new ArrayList<>(); + while (i < recordingList.size() && recordingList.get(i).getStartTimeMs() >= deadLine) { + section.add(recordingList.get(i++)); + } + if (!section.isEmpty()) { + SchedulesHeaderRow headerRow = + new DateHeaderRow( + calculateHeaderDate(deadLine), + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, + section.size(), + section.size()), + section.size(), + deadLine); + add(headerRow); + for (ScheduledRecording recording : section) { + add(new ScheduleRow(recording, headerRow)); + } + } + deadLine -= ONE_DAY_MS; + } + } + + private String calculateHeaderDate(long timeMs) { + int titleIndex = + (int) + ((Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis()) - timeMs) + / ONE_DAY_MS); + String headerDate; + if (titleIndex < mTitles.size()) { + headerDate = mTitles.get(titleIndex); + } else { + headerDate = + DateUtils.formatDateTime( + getContext(), + timeMs, + DateUtils.FORMAT_SHOW_WEEKDAY + | DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_ABBREV_MONTH); + } + return headerDate; + } + + private List<ScheduledRecording> recordedProgramsToScheduledRecordings( + List<RecordedProgram> programs, int maxDays) { + List<ScheduledRecording> result = new ArrayList<>(); + for (RecordedProgram recordedProgram : programs) { + ScheduledRecording scheduledRecording = + recordedProgramsToScheduledRecordings(recordedProgram, maxDays); + if (scheduledRecording != null) { + result.add(scheduledRecording); + } + } + return result; + } + + @Nullable + private ScheduledRecording recordedProgramsToScheduledRecordings( + RecordedProgram program, int maxDays) { + long firstMillisecondToday = Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis()); + if (maxDays + < Utils.computeDateDifference( + program.getStartTimeUtcMillis(), + firstMillisecondToday)) { + return null; + } + ScheduledRecording scheduledRecording = ScheduledRecording.builder(program).build(); + mRecordedProgramScheduleMap.put(program.getId(), scheduledRecording); + return scheduledRecording; + } + + public void onScheduledRecordingAdded(ScheduledRecording schedule) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingAdded: " + schedule); + } + if (findRowByScheduledRecording(schedule) == null + && (schedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED + || schedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED + || schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED)) { + addScheduleRow(schedule); + } + } + + public void onScheduledRecordingAdded(RecordedProgram program) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingAdded: " + program); + } + if (mRecordedProgramScheduleMap.get(program.getId()) != null) { + return; + } + ScheduledRecording schedule = + recordedProgramsToScheduledRecordings(program, MAX_HISTORY_DAYS); + if (schedule == null) { + return; + } + addScheduleRow(schedule); + } + + public void onScheduledRecordingRemoved(ScheduledRecording schedule) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingRemoved: " + schedule); + } + ScheduleRow row = findRowByScheduledRecording(schedule); + if (row != null) { + removeScheduleRow(row); + notifyArrayItemRangeChanged(indexOf(row), 1); + } + } + + public void onScheduledRecordingRemoved(RecordedProgram program) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingRemoved: " + program); + } + ScheduledRecording scheduledRecording = mRecordedProgramScheduleMap.get(program.getId()); + if (scheduledRecording != null) { + mRecordedProgramScheduleMap.remove(program.getId()); + ScheduleRow row = findRowByRecordedProgram(program); + if (row != null) { + removeScheduleRow(row); + notifyArrayItemRangeChanged(indexOf(row), 1); + } + } + } + + public void onScheduledRecordingUpdated(ScheduledRecording schedule) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingUpdated: " + schedule); + } + ScheduleRow row = findRowByScheduledRecording(schedule); + if (row != null) { + row.setSchedule(schedule); + if (schedule.getState() != ScheduledRecording.STATE_RECORDING_FAILED) { + // Only handle failed schedules. Finished schedules are handled as recorded programs + removeScheduleRow(row); + } + notifyArrayItemRangeChanged(indexOf(row), 1); + } + } + + public void onScheduledRecordingUpdated(RecordedProgram program) { + if (DEBUG) { + Log.d(TAG, "onScheduledRecordingUpdated: " + program); + } + ScheduleRow row = findRowByRecordedProgram(program); + if (row != null) { + removeScheduleRow(row); + notifyArrayItemRangeChanged(indexOf(row), 1); + ScheduledRecording schedule = mRecordedProgramScheduleMap.get(program.getId()); + if (schedule != null) { + mRecordedProgramScheduleMap.remove(program.getId()); + } + } + onScheduledRecordingAdded(program); + } + + private void addScheduleRow(ScheduledRecording recording) { + // This method must not be called from inherited class. + SoftPreconditions.checkState(getClass().equals(DvrHistoryRowAdapter.class)); + if (recording != null) { + int pre = -1; + int index = 0; + for (; index < size(); index++) { + if (get(index) instanceof ScheduleRow) { + ScheduleRow scheduleRow = (ScheduleRow) get(index); + if (ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.reversed() + .compare(scheduleRow.getSchedule(), recording) > 0) { + break; + } + pre = index; + } + } + long deadLine = Utils.getFirstMillisecondOfDay(recording.getStartTimeMs()); + if (pre >= 0 && getHeaderRow(pre).getDeadLineMs() == deadLine) { + SchedulesHeaderRow headerRow = ((ScheduleRow) get(pre)).getHeaderRow(); + headerRow.setItemCount(headerRow.getItemCount() + 1); + ScheduleRow addedRow = new ScheduleRow(recording, headerRow); + add(++pre, addedRow); + updateHeaderDescription(headerRow); + } else if (index < size() && getHeaderRow(index).getDeadLineMs() == deadLine) { + SchedulesHeaderRow headerRow = ((ScheduleRow) get(index)).getHeaderRow(); + headerRow.setItemCount(headerRow.getItemCount() + 1); + ScheduleRow addedRow = new ScheduleRow(recording, headerRow); + add(index, addedRow); + updateHeaderDescription(headerRow); + } else { + SchedulesHeaderRow headerRow = + new DateHeaderRow( + calculateHeaderDate(deadLine), + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, 1, 1), + 1, + deadLine); + add(++pre, headerRow); + ScheduleRow addedRow = new ScheduleRow(recording, headerRow); + add(pre, addedRow); + } + } + } + + private DateHeaderRow getHeaderRow(int index) { + return ((DateHeaderRow) ((ScheduleRow) get(index)).getHeaderRow()); + } + + /** Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to. */ + private ScheduleRow findRowByScheduledRecording(ScheduledRecording recording) { + if (recording == null) { + return null; + } + for (int i = 0; i < size(); i++) { + Object item = get(i); + if (item instanceof ScheduleRow && ((ScheduleRow) item).getSchedule() != null) { + if (((ScheduleRow) item).getSchedule().getId() == recording.getId()) { + return (ScheduleRow) item; + } + } + } + return null; + } + + private ScheduleRow findRowByRecordedProgram(RecordedProgram program) { + if (program == null) { + return null; + } + for (int i = 0; i < size(); i++) { + Object item = get(i); + if (item instanceof ScheduleRow) { + ScheduleRow row = (ScheduleRow) item; + if (row.hasRecordedProgram() + && row.getSchedule().getRecordedProgramId() == program.getId()) { + return (ScheduleRow) item; + } + } + } + return null; + } + + private void removeScheduleRow(ScheduleRow scheduleRow) { + // This method must not be called from inherited class. + SoftPreconditions.checkState(getClass().equals(DvrHistoryRowAdapter.class)); + if (scheduleRow != null) { + scheduleRow.setSchedule(null); + SchedulesHeaderRow headerRow = scheduleRow.getHeaderRow(); + remove(scheduleRow); + // Changes the count information of header which the removed row belongs to. + if (headerRow != null) { + int currentCount = headerRow.getItemCount(); + headerRow.setItemCount(--currentCount); + if (headerRow.getItemCount() == 0) { + remove(headerRow); + } else { + replace(indexOf(headerRow), headerRow); + updateHeaderDescription(headerRow); + } + } + } + } + + private void updateHeaderDescription(SchedulesHeaderRow headerRow) { + headerRow.setDescription( + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, + headerRow.getItemCount(), + headerRow.getItemCount())); + } +} diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java index a0410bb3..82b85630 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java +++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java @@ -20,23 +20,19 @@ import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.support.annotation.IntDef; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; import com.android.tv.data.Program; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.provider.EpisodicProgramLoadTask; import com.android.tv.dvr.recorder.SeriesRecordingScheduler; import com.android.tv.dvr.ui.BigArguments; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; -/** - * Activity to show the list of recording schedules. - */ +/** Activity to show the list of recording schedules. */ public class DvrSchedulesActivity extends Activity { /** * The key for the type of the schedules which will be listed in the list. The type of the value @@ -47,9 +43,7 @@ public class DvrSchedulesActivity extends Activity { @Retention(RetentionPolicy.SOURCE) @IntDef({TYPE_FULL_SCHEDULE, TYPE_SERIES_SCHEDULE}) public @interface ScheduleListType {} - /** - * A type which means the activity will display the full scheduled recordings. - */ + /** A type which means the activity will display the full scheduled recordings. */ public static final int TYPE_FULL_SCHEDULE = 0; /** * A type which means the activity will display a scheduled recording list of a series @@ -59,7 +53,7 @@ public class DvrSchedulesActivity extends Activity { @Override public void onCreate(final Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); // Pass null to prevent automatically re-creating fragments super.onCreate(null); setContentView(R.layout.activity_dvr_schedules); @@ -67,20 +61,29 @@ public class DvrSchedulesActivity extends Activity { if (scheduleType == TYPE_FULL_SCHEDULE) { DvrSchedulesFragment schedulesFragment = new DvrSchedulesFragment(); schedulesFragment.setArguments(getIntent().getExtras()); - getFragmentManager().beginTransaction().add( - R.id.fragment_container, schedulesFragment).commit(); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, schedulesFragment) + .commit(); } else if (scheduleType == TYPE_SERIES_SCHEDULE) { - if (BigArguments.getArgument(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS) != null) { + if (BigArguments.getArgument( + DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_PROGRAMS) + != null) { // The programs will be passed to the DvrSeriesSchedulesFragment, so don't need // to reset the BigArguments. showDvrSeriesSchedulesFragment(getIntent().getExtras()); } else { - final ProgressDialog dialog = ProgressDialog.show(this, null, getString( - R.string.dvr_series_progress_message_reading_programs)); - SeriesRecording seriesRecording = getIntent().getExtras() - .getParcelable(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_RECORDING); + final ProgressDialog dialog = + ProgressDialog.show( + this, + null, + getString(R.string.dvr_series_progress_message_reading_programs)); + SeriesRecording seriesRecording = + getIntent() + .getExtras() + .getParcelable( + DvrSeriesSchedulesFragment + .SERIES_SCHEDULES_KEY_SERIES_RECORDING); // To get programs faster, hold the update of the series schedules. SeriesRecordingScheduler.getInstance(this).pauseUpdate(); new EpisodicProgramLoadTask(this, Collections.singletonList(seriesRecording)) { @@ -110,7 +113,9 @@ public class DvrSchedulesActivity extends Activity { private void showDvrSeriesSchedulesFragment(Bundle args) { DvrSeriesSchedulesFragment schedulesFragment = new DvrSeriesSchedulesFragment(); schedulesFragment.setArguments(args); - getFragmentManager().beginTransaction().add( - R.id.fragment_container, schedulesFragment).commit(); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, schedulesFragment) + .commit(); } } diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java index c906c62a..c86721e8 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java +++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java @@ -23,12 +23,9 @@ import android.graphics.RectF; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; - import com.android.tv.R; -/** - * A view used for focus in schedules list. - */ +/** A view used for focus in schedules list. */ public class DvrSchedulesFocusView extends View { private final Paint mPaint; private final RectF mRoundRectF = new RectF(); @@ -76,13 +73,11 @@ public class DvrSchedulesFocusView extends View { private int getRoundRectRadius() { if (TextUtils.equals(mViewTag, mHeaderFocusViewTag)) { - return getResources().getDimensionPixelSize( - R.dimen.dvr_schedules_header_selector_radius); + return getResources() + .getDimensionPixelSize(R.dimen.dvr_schedules_header_selector_radius); } else if (TextUtils.equals(mViewTag, mItemFocusViewTag)) { return getResources().getDimensionPixelSize(R.dimen.dvr_schedules_selector_radius); } return 0; } } - - diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java index 3cbb500a..d97b61f4 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java @@ -18,14 +18,11 @@ package com.android.tv.dvr.ui.list; import android.os.Bundle; import android.support.v17.leanback.widget.ClassPresenterSelector; - import com.android.tv.R; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.DateHeaderRowPresenter; -/** - * A fragment to show the list of schedule recordings. - */ +/** A fragment to show the list of schedule recordings. */ public class DvrSchedulesFragment extends BaseDvrSchedulesFragment { @Override public void onCreate(Bundle savedInstanceState) { @@ -46,8 +43,8 @@ public class DvrSchedulesFragment extends BaseDvrSchedulesFragment { } @Override - public ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelecor) { - return new ScheduleRowAdapter(getContext(), presenterSelecor); + public ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelector) { + return new ScheduleRowAdapter(getContext(), presenterSelector); } @Override @@ -73,11 +70,11 @@ public class DvrSchedulesFragment extends BaseDvrSchedulesFragment { if (args != null) { recording = args.getParcelable(SCHEDULES_KEY_SCHEDULED_RECORDING); } - final int selectedPostion = getRowsAdapter().indexOf( - getRowsAdapter().findRowByScheduledRecording(recording)); + final int selectedPostion = + getRowsAdapter().indexOf(getRowsAdapter().findRowByScheduledRecording(recording)); if (selectedPostion != -1) { return selectedPostion; } return super.getFirstItemPosition(); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java index 57e7a88f..d376e358 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2016 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 -*/ + * Copyright (C) 2016 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.list; @@ -30,10 +30,8 @@ import android.transition.Fade; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.Program; import com.android.tv.dvr.DvrDataManager; @@ -41,25 +39,21 @@ import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.provider.EpisodicProgramLoadTask; import com.android.tv.dvr.ui.BigArguments; - import java.util.Collections; import java.util.List; -/** - * A fragment to show the list of series schedule recordings. - */ +/** A fragment to show the list of series schedule recordings. */ @TargetApi(Build.VERSION_CODES.N) public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { /** - * The key for series recording whose scheduled recording list will be displayed. - * Type: {@link SeriesRecording} + * The key for series recording whose scheduled recording list will be displayed. Type: {@link + * SeriesRecording} */ public static final String SERIES_SCHEDULES_KEY_SERIES_RECORDING = "series_schedules_key_series_recording"; /** - * The key for programs which belong to the series recording whose scheduled recording list - * will be displayed. - * Type: List<{@link Program}> + * The key for programs which belong to the series recording whose scheduled recording list will + * be displayed. Type: List<{@link Program}> */ public static final String SERIES_SCHEDULES_KEY_SERIES_PROGRAMS = "series_schedules_key_series_programs"; @@ -73,7 +67,7 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { private final SeriesRecordingListener mSeriesRecordingListener = new SeriesRecordingListener() { @Override - public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { } + public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {} @Override public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { @@ -101,26 +95,28 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { }; private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final ContentObserver mContentObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - executeProgramLoadingTask(); - } - }; + private final ContentObserver mContentObserver = + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + executeProgramLoadingTask(); + } + }; - private final ChannelDataManager.Listener mChannelListener = new ChannelDataManager.Listener() { - @Override - public void onLoadFinished() { } + private final ChannelDataManager.Listener mChannelListener = + new ChannelDataManager.Listener() { + @Override + public void onLoadFinished() {} - @Override - public void onChannelListUpdated() { - executeProgramLoadingTask(); - } + @Override + public void onChannelListUpdated() { + executeProgramLoadingTask(); + } - @Override - public void onChannelBrowsableChanged() { } - }; + @Override + public void onChannelBrowsableChanged() {} + }; public DvrSeriesSchedulesFragment() { setEnterTransition(new Fade(Fade.IN)); @@ -132,8 +128,8 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { Bundle args = getArguments(); if (args != null) { mSeriesRecording = args.getParcelable(SERIES_SCHEDULES_KEY_SERIES_RECORDING); - mPrograms = (List<Program>) BigArguments.getArgument( - SERIES_SCHEDULES_KEY_SERIES_PROGRAMS); + mPrograms = + (List<Program>) BigArguments.getArgument(SERIES_SCHEDULES_KEY_SERIES_PROGRAMS); BigArguments.reset(); } if (args == null || mPrograms == null) { @@ -144,18 +140,19 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + TvSingletons singletons = TvSingletons.getSingletons(getContext()); mChannelDataManager = singletons.getChannelDataManager(); mChannelDataManager.addListener(mChannelListener); mDvrDataManager = singletons.getDvrDataManager(); mDvrDataManager.addSeriesRecordingListener(mSeriesRecordingListener); - getContext().getContentResolver().registerContentObserver(Programs.CONTENT_URI, true, - mContentObserver); + getContext() + .getContentResolver() + .registerContentObserver(Programs.CONTENT_URI, true, mContentObserver); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { onProgramsUpdated(); return super.onCreateView(inflater, container, savedInstanceState); } @@ -218,14 +215,16 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { if (mProgramLoadTask != null) { mProgramLoadTask.cancel(true); } - mProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) { - @Override - protected void onPostExecute(List<Program> programs) { - mPrograms = programs == null ? Collections.EMPTY_LIST : programs; - onProgramsUpdated(); - } - }; - mProgramLoadTask.setLoadCurrentProgram(true) + mProgramLoadTask = + new EpisodicProgramLoadTask(getContext(), mSeriesRecording) { + @Override + protected void onPostExecute(List<Program> programs) { + mPrograms = programs == null ? Collections.EMPTY_LIST : programs; + onProgramsUpdated(); + } + }; + mProgramLoadTask + .setLoadCurrentProgram(true) .setLoadDisallowedProgram(true) .setLoadScheduledEpisode(true) .setIgnoreChannelOption(true) diff --git a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java index 2af832ec..d5808412 100644 --- a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java +++ b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java @@ -17,29 +17,27 @@ package com.android.tv.dvr.ui.list; import android.content.Context; - import com.android.tv.data.Program; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.ScheduledRecording.Builder; import com.android.tv.dvr.ui.DvrUiHelper; -/** - * A class for the episodic program. - */ +/** A class for the episodic program. */ class EpisodicProgramRow extends ScheduleRow { private final String mInputId; private final Program mProgram; - public EpisodicProgramRow(String inputId, Program program, ScheduledRecording recording, + public EpisodicProgramRow( + String inputId, + Program program, + ScheduledRecording recording, SchedulesHeaderRow headerRow) { super(recording, headerRow); mInputId = inputId; mProgram = program; } - /** - * Returns the program. - */ + /** Returns the program. */ public Program getProgram() { return mProgram; } @@ -82,9 +80,6 @@ class EpisodicProgramRow extends ScheduleRow { @Override public String toString() { - return super.toString() - + "(inputId=" + mInputId - + ",program=" + mProgram - + ")"; + return super.toString() + "(inputId=" + mInputId + ",program=" + mProgram + ")"; } } diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRow.java b/src/com/android/tv/dvr/ui/list/ScheduleRow.java index 91ba393a..b739c18f 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRow.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRow.java @@ -18,14 +18,11 @@ package com.android.tv.dvr.ui.list; import android.content.Context; import android.support.annotation.Nullable; - import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.ui.DvrUiHelper; -/** - * A class for schedule recording row. - */ +/** A class for schedule recording row. */ class ScheduleRow { private final SchedulesHeaderRow mHeaderRow; @Nullable private ScheduledRecording mSchedule; @@ -37,113 +34,89 @@ class ScheduleRow { mHeaderRow = headerRow; } - /** - * Gets which {@link SchedulesHeaderRow} this schedule row belongs to. - */ + /** Gets which {@link SchedulesHeaderRow} this schedule row belongs to. */ public SchedulesHeaderRow getHeaderRow() { return mHeaderRow; } - /** - * Returns the recording schedule. - */ + /** Returns the recording schedule. */ @Nullable public ScheduledRecording getSchedule() { return mSchedule; } - /** - * Checks if the stop recording has been requested or not. - */ + /** Checks if the stop recording has been requested or not. */ public boolean isStopRecordingRequested() { return mStopRecordingRequested; } - /** - * Sets the flag of stop recording request. - */ + /** Sets the flag of stop recording request. */ public void setStopRecordingRequested(boolean stopRecordingRequested) { SoftPreconditions.checkState(!mStartRecordingRequested); mStopRecordingRequested = stopRecordingRequested; } - /** - * Checks if the start recording has been requested or not. - */ + /** Checks if the start recording has been requested or not. */ public boolean isStartRecordingRequested() { return mStartRecordingRequested; } - /** - * Sets the flag of start recording request. - */ + /** Sets the flag of start recording request. */ public void setStartRecordingRequested(boolean startRecordingRequested) { SoftPreconditions.checkState(!mStopRecordingRequested); mStartRecordingRequested = startRecordingRequested; } - /** - * Sets the recording schedule. - */ + /** Sets the recording schedule. */ public void setSchedule(@Nullable ScheduledRecording schedule) { mSchedule = schedule; } - /** - * Returns the channel ID. - */ + /** Returns the channel ID. */ public long getChannelId() { return mSchedule != null ? mSchedule.getChannelId() : -1; } - /** - * Returns the start time. - */ + /** Returns the start time. */ public long getStartTimeMs() { return mSchedule != null ? mSchedule.getStartTimeMs() : -1; } - /** - * Returns the end time. - */ + /** Returns the end time. */ public long getEndTimeMs() { return mSchedule != null ? mSchedule.getEndTimeMs() : -1; } - /** - * Returns the duration. - */ + /** Returns the duration. */ public final long getDuration() { return getEndTimeMs() - getStartTimeMs(); } - /** - * Checks if the program is on air. - */ + /** Checks if the program is on air. */ public final boolean isOnAir() { long currentTimeMs = System.currentTimeMillis(); return getStartTimeMs() <= currentTimeMs && getEndTimeMs() > currentTimeMs; } - /** - * Checks if the schedule is not started. - */ + /** Checks if the schedule is not started. */ public final boolean isRecordingNotStarted() { return mSchedule != null && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED; } - /** - * Checks if the schedule is in progress. - */ + /** Checks if the schedule is in progress. */ public final boolean isRecordingInProgress() { return mSchedule != null && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS; } - /** - * Checks if the schedule has been canceled or not. - */ + /** Checks if the schedule is failed. */ + public final boolean isRecordingFailed() { + return mSchedule != null + && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED; + } + + /** Checks if the schedule has been canceled or not. */ public final boolean isScheduleCanceled() { return mSchedule != null && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED; @@ -152,28 +125,29 @@ class ScheduleRow { public boolean isRecordingFinished() { return mSchedule != null && (mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED - || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED - || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED); + || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED + || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED); } - /** - * Creates and returns the new schedule with the existing information. - */ + public boolean hasRecordedProgram() { + return mSchedule != null + && mSchedule.getRecordedProgramId() != null + && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED; + } + + /** Creates and returns the new schedule with the existing information. */ public ScheduledRecording.Builder createNewScheduleBuilder() { return mSchedule != null ? ScheduledRecording.buildFrom(mSchedule) : null; } - /** - * Returns the program title with episode number. - */ + /** Returns the program title with episode number. */ public String getProgramTitleWithEpisodeNumber(Context context) { - return mSchedule != null ? DvrUiHelper.getStyledTitleWithEpisodeNumber(context, - mSchedule, 0).toString() : null; + return mSchedule != null + ? DvrUiHelper.getStyledTitleWithEpisodeNumber(context, mSchedule, 0).toString() + : null; } - /** - * Returns the program title including the season/episode number. - */ + /** Returns the program title including the season/episode number. */ public String getEpisodeDisplayTitle(Context context) { return mSchedule != null ? mSchedule.getEpisodeDisplayTitle(context) : null; } @@ -181,15 +155,16 @@ class ScheduleRow { @Override public String toString() { return getClass().getSimpleName() - + "(schedule=" + mSchedule - + ",stopRecordingRequested=" + mStopRecordingRequested - + ",startRecordingRequested=" + mStartRecordingRequested + + "(schedule=" + + mSchedule + + ",stopRecordingRequested=" + + mStopRecordingRequested + + ",startRecordingRequested=" + + mStartRecordingRequested + ")"; } - /** - * Checks if the {@code schedule} is for the program or channel. - */ + /** Checks if the {@code schedule} is for the program or channel. */ public boolean matchSchedule(ScheduledRecording schedule) { if (mSchedule == null) { return false; diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java index 97d60473..ef4a4337 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java @@ -16,7 +16,9 @@ package com.android.tv.dvr.ui.list; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build.VERSION_CODES; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -27,11 +29,11 @@ import android.util.ArraySet; import android.util.Log; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow; import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow; import com.android.tv.util.Utils; import java.util.ArrayList; @@ -40,14 +42,14 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -/** - * An adapter for {@link ScheduleRow}. - */ +/** An adapter for {@link ScheduleRow}. */ +@TargetApi(VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated class ScheduleRowAdapter extends ArrayObjectAdapter { private static final String TAG = "ScheduleRowAdapter"; private static final boolean DEBUG = false; - private final static long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1); + private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1); private static final int MSG_UPDATE_ROW = 1; @@ -55,16 +57,17 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { private final List<String> mTitles = new ArrayList<>(); private final Set<ScheduleRow> mPendingUpdate = new ArraySet<>(); - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_UPDATE_ROW) { - long currentTimeMs = System.currentTimeMillis(); - handleUpdateRow(currentTimeMs); - sendNextUpdateMessage(currentTimeMs); - } - } - }; + private final Handler mHandler = + new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_UPDATE_ROW) { + long currentTimeMs = System.currentTimeMillis(); + handleUpdateRow(currentTimeMs); + sendNextUpdateMessage(currentTimeMs); + } + } + }; public ScheduleRowAdapter(Context context, ClassPresenterSelector classPresenterSelector) { super(classPresenterSelector); @@ -73,37 +76,41 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { mTitles.add(mContext.getString(R.string.dvr_date_tomorrow)); } - /** - * Returns context. - */ + /** Returns context. */ protected Context getContext() { return mContext; } - /** - * Starts schedule row adapter. - */ + /** Starts schedule row adapter. */ public void start() { clear(); - List<ScheduledRecording> recordingList = TvApplication.getSingletons(mContext) - .getDvrDataManager().getNonStartedScheduledRecordings(); - recordingList.addAll(TvApplication.getSingletons(mContext).getDvrDataManager() - .getStartedRecordings()); - Collections.sort(recordingList, - ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); + List<ScheduledRecording> recordingList = + TvSingletons.getSingletons(mContext) + .getDvrDataManager() + .getNonStartedScheduledRecordings(); + recordingList.addAll( + TvSingletons.getSingletons(mContext).getDvrDataManager().getStartedRecordings()); + Collections.sort( + recordingList, ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); long deadLine = Utils.getLastMillisecondOfDay(System.currentTimeMillis()); - for (int i = 0; i < recordingList.size();) { + for (int i = 0; i < recordingList.size(); ) { ArrayList<ScheduledRecording> section = new ArrayList<>(); while (i < recordingList.size() && recordingList.get(i).getStartTimeMs() < deadLine) { section.add(recordingList.get(i++)); } if (!section.isEmpty()) { - SchedulesHeaderRow headerRow = new DateHeaderRow(calculateHeaderDate(deadLine), - mContext.getResources().getQuantityString( - R.plurals.dvr_schedules_section_subtitle, section.size(), section.size()), - section.size(), deadLine); + SchedulesHeaderRow headerRow = + new DateHeaderRow( + calculateHeaderDate(deadLine), + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, + section.size(), + section.size()), + section.size(), + deadLine); add(headerRow); - for(ScheduledRecording recording : section){ + for (ScheduledRecording recording : section) { add(new ScheduleRow(recording, headerRow)); } } @@ -113,25 +120,29 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } private String calculateHeaderDate(long deadLine) { - int titleIndex = (int) ((deadLine - - Utils.getLastMillisecondOfDay(System.currentTimeMillis())) / ONE_DAY_MS); + int titleIndex = + (int) + ((deadLine - Utils.getLastMillisecondOfDay(System.currentTimeMillis())) + / ONE_DAY_MS); String headerDate; if (titleIndex < mTitles.size()) { headerDate = mTitles.get(titleIndex); } else { - headerDate = DateUtils.formatDateTime(getContext(), deadLine, - DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_ABBREV_MONTH); + headerDate = + DateUtils.formatDateTime( + getContext(), + deadLine, + DateUtils.FORMAT_SHOW_WEEKDAY + | DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_ABBREV_MONTH); } return headerDate; } - /** - * Stops schedules row adapter. - */ + /** Stops schedules row adapter. */ public void stop() { mHandler.removeCallbacksAndMessages(null); - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); + DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); for (int i = 0; i < size(); i++) { if (get(i) instanceof ScheduleRow) { ScheduleRow row = (ScheduleRow) get(i); @@ -142,9 +153,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } } - /** - * Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to. - */ + /** Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to. */ public ScheduleRow findRowByScheduledRecording(ScheduledRecording recording) { if (recording == null) { return null; @@ -167,7 +176,8 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { continue; } ScheduleRow row = (ScheduleRow) item; - if (row.getSchedule() != null && row.isStartRecordingRequested() + if (row.getSchedule() != null + && row.isStartRecordingRequested() && row.matchSchedule(schedule)) { return row; } @@ -185,7 +195,8 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { if (get(index) instanceof ScheduleRow) { ScheduleRow scheduleRow = (ScheduleRow) get(index); if (ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.compare( - scheduleRow.getSchedule(), recording) > 0) { + scheduleRow.getSchedule(), recording) + > 0) { break; } pre = index; @@ -205,9 +216,14 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { add(index, addedRow); updateHeaderDescription(headerRow); } else { - SchedulesHeaderRow headerRow = new DateHeaderRow(calculateHeaderDate(deadLine), - mContext.getResources().getQuantityString( - R.plurals.dvr_schedules_section_subtitle, 1, 1), 1, deadLine); + SchedulesHeaderRow headerRow = + new DateHeaderRow( + calculateHeaderDate(deadLine), + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, 1, 1), + 1, + deadLine); add(++pre, headerRow); ScheduleRow addedRow = new ScheduleRow(recording, headerRow); add(pre, addedRow); @@ -241,14 +257,15 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } private void updateHeaderDescription(SchedulesHeaderRow headerRow) { - headerRow.setDescription(mContext.getResources().getQuantityString( - R.plurals.dvr_schedules_section_subtitle, - headerRow.getItemCount(), headerRow.getItemCount())); + headerRow.setDescription( + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_section_subtitle, + headerRow.getItemCount(), + headerRow.getItemCount())); } - /** - * Called when a schedule recording is added to dvr date manager. - */ + /** Called when a schedule recording is added to dvr date manager. */ public void onScheduledRecordingAdded(ScheduledRecording schedule) { if (DEBUG) Log.d(TAG, "onScheduledRecordingAdded: " + schedule); ScheduleRow row = findRowWithStartRequest(schedule); @@ -263,9 +280,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } } - /** - * Called when a schedule recording is removed from dvr date manager. - */ + /** Called when a schedule recording is removed from dvr date manager. */ public void onScheduledRecordingRemoved(ScheduledRecording schedule) { if (DEBUG) Log.d(TAG, "onScheduledRecordingRemoved: " + schedule); ScheduleRow row = findRowByScheduledRecording(schedule); @@ -276,9 +291,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } } - /** - * Called when a schedule recording is updated in dvr date manager. - */ + /** Called when a schedule recording is updated in dvr date manager. */ public void onScheduledRecordingUpdated(ScheduledRecording schedule, boolean conflictChange) { if (DEBUG) Log.d(TAG, "onScheduledRecordingUpdated: " + schedule); ScheduleRow row = findRowByScheduledRecording(schedule); @@ -329,9 +342,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } } - /** - * Checks if there is a row which requested start/stop recording. - */ + /** Checks if there is a row which requested start/stop recording. */ protected boolean isStartOrStopRequested() { for (int i = 0; i < size(); i++) { Object item = get(i); @@ -345,16 +356,12 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { return false; } - /** - * Delays update of the row. - */ + /** Delays update of the row. */ protected void addPendingUpdate(ScheduleRow row) { mPendingUpdate.add(row); } - /** - * Executes the pending updates. - */ + /** Executes the pending updates. */ protected void executePendingUpdate() { for (ScheduleRow row : mPendingUpdate) { int index = indexOf(row); @@ -365,21 +372,17 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { mPendingUpdate.clear(); } - /** - * To check whether the recording should be kept or not. - */ + /** To check whether the recording should be kept or not. */ protected boolean willBeKept(ScheduledRecording schedule) { // CANCELED state means that the schedule was removed temporarily, which should be shown // in the list so that the user can reschedule it. return schedule.getEndTimeMs() > System.currentTimeMillis() && (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS - || schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || schedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED); + || schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED + || schedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED); } - /** - * Handle the message to update/remove rows. - */ + /** Handle the message to update/remove rows. */ protected void handleUpdateRow(long currentTimeMs) { for (int i = 0; i < size(); i++) { Object item = get(i); @@ -392,9 +395,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { } } - /** - * Returns the next update time. Return {@link Long#MAX_VALUE} if no timer is necessary. - */ + /** Returns the next update time. Return {@link Long#MAX_VALUE} if no timer is necessary. */ protected long getNextTimerMs(long currentTimeMs) { long earliest = Long.MAX_VALUE; for (int i = 0; i < size(); i++) { @@ -411,15 +412,12 @@ class ScheduleRowAdapter extends ArrayObjectAdapter { return earliest; } - /** - * Send update message at the time returned by {@link #getNextTimerMs}. - */ + /** Send update message at the time returned by {@link #getNextTimerMs}. */ protected final void sendNextUpdateMessage(long currentTimeMs) { mHandler.removeMessages(MSG_UPDATE_ROW); long nextTime = getNextTimerMs(currentTimeMs); if (nextTime != Long.MAX_VALUE) { - mHandler.sendEmptyMessageDelayed(MSG_UPDATE_ROW, - nextTime - System.currentTimeMillis()); + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_ROW, nextTime - System.currentTimeMillis()); } } } diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java index dc4e3c41..38d3d582 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java @@ -18,7 +18,6 @@ package com.android.tv.dvr.ui.list; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; @@ -37,11 +36,11 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvFeatures; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.data.api.Channel; import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrScheduleManager; @@ -50,21 +49,22 @@ import com.android.tv.dvr.ui.DvrStopRecordingFragment; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.util.ToastUtils; import com.android.tv.util.Utils; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; -/** - * A RowPresenter for {@link ScheduleRow}. - */ +/** A RowPresenter for {@link ScheduleRow}. */ @TargetApi(Build.VERSION_CODES.N) class ScheduleRowPresenter extends RowPresenter { private static final String TAG = "ScheduleRowPresenter"; @Retention(RetentionPolicy.SOURCE) - @IntDef({ACTION_START_RECORDING, ACTION_STOP_RECORDING, ACTION_CREATE_SCHEDULE, - ACTION_REMOVE_SCHEDULE}) + @IntDef({ + ACTION_START_RECORDING, + ACTION_STOP_RECORDING, + ACTION_CREATE_SCHEDULE, + ACTION_REMOVE_SCHEDULE + }) public @interface ScheduleRowAction {} /** An action to start recording. */ public static final int ACTION_START_RECORDING = 1; @@ -85,9 +85,7 @@ class ScheduleRowPresenter extends RowPresenter { private int mLastFocusedViewId; - /** - * A ViewHolder for {@link ScheduleRow} - */ + /** A ViewHolder for {@link ScheduleRow} */ public static class ScheduleRowViewHolder extends RowPresenter.ViewHolder { private ScheduleRowPresenter mPresenter; @ScheduleRowAction private int[] mActions; @@ -118,29 +116,30 @@ class ScheduleRowPresenter extends RowPresenter { new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean focused) { - view.post(new Runnable() { - @Override - public void run() { - if (view.isFocused()) { - mPresenter.mLastFocusedViewId = view.getId(); - } - updateSelector(); - } - }); + view.post( + new Runnable() { + @Override + public void run() { + if (view.isFocused()) { + mPresenter.mLastFocusedViewId = view.getId(); + } + updateSelector(); + } + }); } }; public ScheduleRowViewHolder(View view, ScheduleRowPresenter presenter) { super(view); mPresenter = presenter; - mLtr = view.getContext().getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mLtr = + view.getContext().getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_LTR; mInfoContainer = (LinearLayout) view.findViewById(R.id.info_container); - mSecondActionContainer = (RelativeLayout) view.findViewById( - R.id.action_second_container); + mSecondActionContainer = + (RelativeLayout) view.findViewById(R.id.action_second_container); mSecondActionView = (ImageView) view.findViewById(R.id.action_second); - mFirstActionContainer = (RelativeLayout) view.findViewById( - R.id.action_first_container); + mFirstActionContainer = (RelativeLayout) view.findViewById(R.id.action_first_container); mFirstActionView = (ImageView) view.findViewById(R.id.action_first); mSelectorView = view.findViewById(R.id.selector); mTimeView = (TextView) view.findViewById(R.id.time); @@ -151,47 +150,48 @@ class ScheduleRowPresenter extends RowPresenter { Resources res = view.getResources(); mSelectorTranslationDelta = res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin) - - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_focus_translation_delta); - mSelectorWidthDelta = res.getDimensionPixelSize( - R.dimen.dvr_schedules_item_focus_width_delta); + - res.getDimensionPixelSize( + R.dimen.dvr_schedules_item_focus_translation_delta); + mSelectorWidthDelta = + res.getDimensionPixelSize(R.dimen.dvr_schedules_item_focus_width_delta); mRoundRectRadius = res.getDimensionPixelSize(R.dimen.dvr_schedules_selector_radius); - int fullWidth = res.getDimensionPixelSize( - R.dimen.dvr_schedules_item_width) - - 2 * res.getDimensionPixelSize(R.dimen.dvr_schedules_layout_padding); + int fullWidth = + res.getDimensionPixelSize(R.dimen.dvr_schedules_item_width) + - 2 * res.getDimensionPixelSize(R.dimen.dvr_schedules_layout_padding); mInfoContainerTargetWidthWithNoAction = fullWidth + 2 * mRoundRectRadius; - mInfoContainerTargetWidthWithOneAction = fullWidth - - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin) - - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_delete_width) - + mRoundRectRadius + mSelectorWidthDelta; - mInfoContainerTargetWidthWithTwoAction = mInfoContainerTargetWidthWithOneAction - - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin) - - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_icon_size); + mInfoContainerTargetWidthWithOneAction = + fullWidth + - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin) + - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_delete_width) + + mRoundRectRadius + + mSelectorWidthDelta; + mInfoContainerTargetWidthWithTwoAction = + mInfoContainerTargetWidthWithOneAction + - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin) + - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_icon_size); mInfoContainer.setOnFocusChangeListener(mOnFocusChangeListener); mFirstActionContainer.setOnFocusChangeListener(mOnFocusChangeListener); mSecondActionContainer.setOnFocusChangeListener(mOnFocusChangeListener); } - /** - * Returns time view. - */ + /** Returns time view. */ public TextView getTimeView() { return mTimeView; } - /** - * Returns title view. - */ + /** Returns title view. */ public TextView getProgramTitleView() { return mProgramTitleView; } private void updateSelector() { - int animationDuration = mSelectorView.getResources().getInteger( - android.R.integer.config_shortAnimTime); + int animationDuration = + mSelectorView.getResources().getInteger(android.R.integer.config_shortAnimTime); DecelerateInterpolator interpolator = new DecelerateInterpolator(); - if (mInfoContainer.isFocused() || mSecondActionContainer.isFocused() + if (mInfoContainer.isFocused() + || mSecondActionContainer.isFocused() || mFirstActionContainer.isFocused()) { final ViewGroup.LayoutParams lp = mSelectorView.getLayoutParams(); final int targetWidth; @@ -208,33 +208,50 @@ class ScheduleRowPresenter extends RowPresenter { } else if (mSecondActionContainer.isFocused()) { targetWidth = Math.max(mSecondActionContainer.getWidth(), 2 * mRoundRectRadius); } else { - targetWidth = mFirstActionContainer.getWidth() + mRoundRectRadius - + mSelectorTranslationDelta; + targetWidth = + mFirstActionContainer.getWidth() + + mRoundRectRadius + + mSelectorTranslationDelta; } float targetTranslationX; if (mInfoContainer.isFocused()) { - targetTranslationX = mLtr ? mInfoContainer.getLeft() - mRoundRectRadius - - mSelectorView.getLeft() : - mInfoContainer.getRight() + mRoundRectRadius - mSelectorView.getRight(); + targetTranslationX = + mLtr + ? mInfoContainer.getLeft() + - mRoundRectRadius + - mSelectorView.getLeft() + : mInfoContainer.getRight() + + mRoundRectRadius + - mSelectorView.getRight(); } else if (mSecondActionContainer.isFocused()) { if (mSecondActionContainer.getWidth() > 2 * mRoundRectRadius) { - targetTranslationX = mLtr ? mSecondActionContainer.getLeft() - - mSelectorView.getLeft() - : mSecondActionContainer.getRight() - mSelectorView.getRight(); + targetTranslationX = + mLtr + ? mSecondActionContainer.getLeft() - mSelectorView.getLeft() + : mSecondActionContainer.getRight() + - mSelectorView.getRight(); } else { - targetTranslationX = mLtr ? mSecondActionContainer.getLeft() - - (mRoundRectRadius - mSecondActionContainer.getWidth() / 2) - - mSelectorView.getLeft() - : mSecondActionContainer.getRight() + - (mRoundRectRadius - mSecondActionContainer.getWidth() / 2) - - mSelectorView.getRight(); + targetTranslationX = + mLtr + ? mSecondActionContainer.getLeft() + - (mRoundRectRadius + - mSecondActionContainer.getWidth() / 2) + - mSelectorView.getLeft() + : mSecondActionContainer.getRight() + + (mRoundRectRadius + - mSecondActionContainer.getWidth() / 2) + - mSelectorView.getRight(); } } else { - targetTranslationX = mLtr ? mFirstActionContainer.getLeft() - - mSelectorTranslationDelta - mSelectorView.getLeft() - : mFirstActionContainer.getRight() + mSelectorTranslationDelta - - mSelectorView.getRight(); + targetTranslationX = + mLtr + ? mFirstActionContainer.getLeft() + - mSelectorTranslationDelta + - mSelectorView.getLeft() + : mFirstActionContainer.getRight() + + mSelectorTranslationDelta + - mSelectorView.getRight(); } if (mSelectorView.getAlpha() == 0) { @@ -246,57 +263,72 @@ class ScheduleRowPresenter extends RowPresenter { // animate the selector in and to the proper width and translation X. final float deltaWidth = lp.width - targetWidth; mSelectorView.animate().cancel(); - mSelectorView.animate().translationX(targetTranslationX).alpha(1f) - .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - // Set width to the proper width for this animation step. - lp.width = targetWidth + Math.round( - deltaWidth * (1f - animation.getAnimatedFraction())); - mSelectorView.requestLayout(); - } - }).setDuration(animationDuration).setInterpolator(interpolator).start(); + mSelectorView + .animate() + .translationX(targetTranslationX) + .alpha(1f) + .setUpdateListener( + animation -> { + // Set width to the proper width for this animation step. + float fraction = 1f - animation.getAnimatedFraction(); + lp.width = targetWidth + Math.round(deltaWidth * fraction); + mSelectorView.requestLayout(); + }) + .setDuration(animationDuration) + .setInterpolator(interpolator) + .start(); if (mPendingAnimationRunnable != null) { mPendingAnimationRunnable.run(); mPendingAnimationRunnable = null; } } else { mSelectorView.animate().cancel(); - mSelectorView.animate().alpha(0f).setDuration(animationDuration) - .setInterpolator(interpolator).setUpdateListener(null).start(); + mSelectorView + .animate() + .alpha(0f) + .setDuration(animationDuration) + .setInterpolator(interpolator) + .setUpdateListener(null) + .start(); } } - /** - * Grey out the information body. - */ + /** Grey out the information body. */ public void greyOutInfo() { - mTimeView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info_grey, null)); - mProgramTitleView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info_grey, null)); - mInfoSeparatorView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info_grey, null)); - mChannelNameView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info_grey, null)); - mConflictInfoView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info_grey, null)); + mTimeView.setTextColor( + mInfoContainer + .getResources() + .getColor(R.color.dvr_schedules_item_info_grey, null)); + mProgramTitleView.setTextColor( + mInfoContainer + .getResources() + .getColor(R.color.dvr_schedules_item_info_grey, null)); + mInfoSeparatorView.setTextColor( + mInfoContainer + .getResources() + .getColor(R.color.dvr_schedules_item_info_grey, null)); + mChannelNameView.setTextColor( + mInfoContainer + .getResources() + .getColor(R.color.dvr_schedules_item_info_grey, null)); + mConflictInfoView.setTextColor( + mInfoContainer + .getResources() + .getColor(R.color.dvr_schedules_item_info_grey, null)); } - /** - * Reverse grey out operation. - */ + /** Reverse grey out operation. */ public void whiteBackInfo() { - mTimeView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info, null)); - mProgramTitleView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_main, null)); - mInfoSeparatorView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info, null)); - mChannelNameView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info, null)); - mConflictInfoView.setTextColor(mInfoContainer.getResources().getColor(R.color - .dvr_schedules_item_info, null)); + mTimeView.setTextColor( + mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null)); + mProgramTitleView.setTextColor( + mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_main, null)); + mInfoSeparatorView.setTextColor( + mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null)); + mChannelNameView.setTextColor( + mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null)); + mConflictInfoView.setTextColor( + mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null)); } } @@ -304,32 +336,29 @@ class ScheduleRowPresenter extends RowPresenter { setHeaderPresenter(null); setSelectEffectEnabled(false); mContext = context; - mDvrManager = TvApplication.getSingletons(context).getDvrManager(); - mDvrScheduleManager = TvApplication.getSingletons(context).getDvrScheduleManager(); - mTunerConflictWillNotBeRecordedInfo = mContext.getString( - R.string.dvr_schedules_tuner_conflict_will_not_be_recorded_info); - mTunerConflictWillBePartiallyRecordedInfo = mContext.getString( - R.string.dvr_schedules_tuner_conflict_will_be_partially_recorded); - mAnimationDuration = mContext.getResources().getInteger( - android.R.integer.config_shortAnimTime); + mDvrManager = TvSingletons.getSingletons(context).getDvrManager(); + mDvrScheduleManager = TvSingletons.getSingletons(context).getDvrScheduleManager(); + mTunerConflictWillNotBeRecordedInfo = + mContext.getString(R.string.dvr_schedules_tuner_conflict_will_not_be_recorded_info); + mTunerConflictWillBePartiallyRecordedInfo = + mContext.getString( + R.string.dvr_schedules_tuner_conflict_will_be_partially_recorded); + mAnimationDuration = + mContext.getResources().getInteger(android.R.integer.config_shortAnimTime); } @Override public ViewHolder createRowViewHolder(ViewGroup parent) { - return onGetScheduleRowViewHolder(LayoutInflater.from(mContext) - .inflate(R.layout.dvr_schedules_item, parent, false)); + return onGetScheduleRowViewHolder( + LayoutInflater.from(mContext).inflate(R.layout.dvr_schedules_item, parent, false)); } - /** - * Returns context. - */ + /** Returns context. */ protected Context getContext() { return mContext; } - /** - * Returns DVR manager. - */ + /** Returns DVR manager. */ protected DvrManager getDvrManager() { return mDvrManager; } @@ -341,54 +370,77 @@ class ScheduleRowPresenter extends RowPresenter { ScheduleRow row = (ScheduleRow) item; @ScheduleRowAction int[] actions = getAvailableActions(row); viewHolder.mActions = actions; - viewHolder.mInfoContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (isInfoClickable(row)) { - onInfoClicked(row); - } - } - }); + viewHolder.mInfoContainer.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + if (isInfoClickable(row)) { + onInfoClicked(row); + } + } + }); - viewHolder.mFirstActionContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onActionClicked(actions[0], row); - } - }); + viewHolder.mFirstActionContainer.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + onActionClicked(actions[0], row); + } + }); - viewHolder.mSecondActionContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onActionClicked(actions[1], row); - } - }); + viewHolder.mSecondActionContainer.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + onActionClicked(actions[1], row); + } + }); viewHolder.mTimeView.setText(onGetRecordingTimeText(row)); String programInfoText = onGetProgramInfoText(row); if (TextUtils.isEmpty(programInfoText)) { int durationMins = Math.max(1, Utils.getRoundOffMinsFromMs(row.getDuration())); - programInfoText = mContext.getResources().getQuantityString( - R.plurals.dvr_schedules_recording_duration, durationMins, durationMins); + programInfoText = + mContext.getResources() + .getQuantityString( + R.plurals.dvr_schedules_recording_duration, + durationMins, + durationMins); } String channelName = getChannelNameText(row); viewHolder.mProgramTitleView.setText(programInfoText); - viewHolder.mInfoSeparatorView.setVisibility((!TextUtils.isEmpty(programInfoText) - && !TextUtils.isEmpty(channelName)) ? View.VISIBLE : View.GONE); + viewHolder.mInfoSeparatorView.setVisibility( + (!TextUtils.isEmpty(programInfoText) && !TextUtils.isEmpty(channelName)) + ? View.VISIBLE + : View.GONE); viewHolder.mChannelNameView.setText(channelName); if (actions != null) { switch (actions.length) { case 2: viewHolder.mSecondActionView.setImageResource(getImageForAction(actions[1])); - // pass through + // fall through case 1: viewHolder.mFirstActionView.setImageResource(getImageForAction(actions[0])); break; + default: // fall out } } - if (mDvrManager.isConflicting(row.getSchedule())) { + ScheduledRecording schedule = row.getSchedule(); + if (mDvrManager.isConflicting(schedule) + || (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext()) + && schedule != null + && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED)) { String conflictInfo; - if (mDvrScheduleManager.isPartiallyConflicting(row.getSchedule())) { + if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext()) + && schedule != null + && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) { + // TODO(b/72638385): show real error messages + // TODO(b/72638385): use a better name for ConflictInfoXXX + conflictInfo = "Failed"; + if (schedule.getFailedReason() != null) { + conflictInfo += " (Error code: " + schedule.getFailedReason() + ")"; + } + } else if (mDvrScheduleManager.isPartiallyConflicting(row.getSchedule())) { conflictInfo = mTunerConflictWillBePartiallyRecordedInfo; } else { conflictInfo = mTunerConflictWillNotBeRecordedInfo; @@ -422,51 +474,48 @@ class ScheduleRowPresenter extends RowPresenter { } } - /** - * Returns view holder for schedule row. - */ + /** Returns view holder for schedule row. */ protected ScheduleRowViewHolder onGetScheduleRowViewHolder(View view) { return new ScheduleRowViewHolder(view, this); } - /** - * Returns time text for time view from scheduled recording. - */ + /** Returns time text for time view from scheduled recording. */ protected String onGetRecordingTimeText(ScheduleRow row) { - return Utils.getDurationString(mContext, row.getStartTimeMs(), row.getEndTimeMs(), true, - false, true, 0); + return Utils.getDurationString( + mContext, row.getStartTimeMs(), row.getEndTimeMs(), true, false, true, 0); } - /** - * Returns program info text for program title view. - */ + /** Returns program info text for program title view. */ protected String onGetProgramInfoText(ScheduleRow row) { return row.getProgramTitleWithEpisodeNumber(mContext); } private String getChannelNameText(ScheduleRow row) { - Channel channel = TvApplication.getSingletons(mContext).getChannelDataManager() - .getChannel(row.getChannelId()); - return channel == null ? null : - TextUtils.isEmpty(channel.getDisplayName()) ? channel.getDisplayNumber() : - channel.getDisplayName().trim() + " " + channel.getDisplayNumber(); + Channel channel = + TvSingletons.getSingletons(mContext) + .getChannelDataManager() + .getChannel(row.getChannelId()); + return channel == null + ? null + : TextUtils.isEmpty(channel.getDisplayName()) + ? channel.getDisplayNumber() + : channel.getDisplayName().trim() + " " + channel.getDisplayNumber(); } - /** - * Called when user click Info in {@link ScheduleRow}. - */ + /** Called when user click Info in {@link ScheduleRow}. */ protected void onInfoClicked(ScheduleRow row) { DvrUiHelper.startDetailsActivity((Activity) mContext, row.getSchedule(), null, true); } private boolean isInfoClickable(ScheduleRow row) { - return row.getSchedule() != null - && (row.getSchedule().isNotStarted() || row.getSchedule().isInProgress()); + ScheduledRecording schedule = row.getSchedule(); + return schedule != null + && (schedule.isNotStarted() + || schedule.isInProgress() + || schedule.isFinished()); } - /** - * Called when the button in a row is clicked. - */ + /** Called when the button in a row is clicked. */ protected void onActionClicked(@ScheduleRowAction final int action, ScheduleRow row) { switch (action) { case ACTION_START_RECORDING: @@ -481,12 +530,11 @@ class ScheduleRowPresenter extends RowPresenter { case ACTION_REMOVE_SCHEDULE: onRemoveSchedule(row); break; + default: // fall out } } - /** - * Action handler for {@link #ACTION_START_RECORDING}. - */ + /** Action handler for {@link #ACTION_START_RECORDING}. */ protected void onStartRecording(ScheduleRow row) { ScheduledRecording schedule = row.getSchedule(); if (schedule == null) { @@ -495,12 +543,16 @@ class ScheduleRowPresenter extends RowPresenter { } // Checks if there are current recordings that will be stopped by schedule this program. // If so, shows confirmation dialog to users. - List<ScheduledRecording> conflictSchedules = mDvrScheduleManager.getConflictingSchedules( - schedule.getChannelId(), System.currentTimeMillis(), schedule.getEndTimeMs()); + List<ScheduledRecording> conflictSchedules = + mDvrScheduleManager.getConflictingSchedules( + schedule.getChannelId(), + System.currentTimeMillis(), + schedule.getEndTimeMs()); for (int i = conflictSchedules.size() - 1; i >= 0; i--) { ScheduledRecording conflictSchedule = conflictSchedules.get(i); if (conflictSchedule.isInProgress()) { - DvrUiHelper.showStopRecordingDialog((Activity) mContext, + DvrUiHelper.showStopRecordingDialog( + (Activity) mContext, conflictSchedule.getChannelId(), DvrStopRecordingFragment.REASON_ON_CONFLICT, new HalfSizedDialogFragment.OnActionClickListener() { @@ -523,26 +575,27 @@ class ScheduleRowPresenter extends RowPresenter { if (row.isRecordingNotStarted()) { mDvrManager.setHighestPriority(row.getSchedule()); } else if (row.isRecordingFinished()) { - mDvrManager.addSchedule(ScheduledRecording.buildFrom(row.getSchedule()) - .setId(ScheduledRecording.ID_NOT_SET) - .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED) - .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule())) - .build()); + mDvrManager.addSchedule( + ScheduledRecording.buildFrom(row.getSchedule()) + .setId(ScheduledRecording.ID_NOT_SET) + .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED) + .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule())) + .build()); } else { - SoftPreconditions.checkState(false, TAG, "Invalid row state to start recording: " - + row); + SoftPreconditions.checkState( + false, TAG, "Invalid row state to start recording: " + row); return; } - String msg = mContext.getString(R.string.dvr_msg_current_program_scheduled, - row.getSchedule().getProgramTitle(), - Utils.toTimeString(row.getEndTimeMs(), false)); + String msg = + mContext.getString( + R.string.dvr_msg_current_program_scheduled, + row.getSchedule().getProgramTitle(), + Utils.toTimeString(row.getEndTimeMs(), false)); ToastUtils.show(mContext, msg, Toast.LENGTH_SHORT); } } - /** - * Action handler for {@link #ACTION_STOP_RECORDING}. - */ + /** Action handler for {@link #ACTION_STOP_RECORDING}. */ protected void onStopRecording(ScheduleRow row) { if (row.getSchedule() == null) { // This row has been deleted. @@ -555,15 +608,15 @@ class ScheduleRowPresenter extends RowPresenter { if (TextUtils.isEmpty(deletedInfo)) { deletedInfo = getChannelNameText(row); } - ToastUtils.show(mContext, mContext.getResources() - .getString(R.string.dvr_schedules_deletion_info, deletedInfo), + ToastUtils.show( + mContext, + mContext.getResources() + .getString(R.string.dvr_schedules_deletion_info, deletedInfo), Toast.LENGTH_SHORT); } } - /** - * Action handler for {@link #ACTION_CREATE_SCHEDULE}. - */ + /** Action handler for {@link #ACTION_CREATE_SCHEDULE}. */ protected void onCreateSchedule(ScheduleRow row) { if (row.getSchedule() == null) { // This row has been deleted. @@ -571,12 +624,15 @@ class ScheduleRowPresenter extends RowPresenter { } if (!row.isOnAir()) { if (row.isScheduleCanceled()) { - mDvrManager.updateScheduledRecording(ScheduledRecording.buildFrom(row.getSchedule()) - .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED) - .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule())) - .build()); - String msg = mContext.getString(R.string.dvr_msg_program_scheduled, - row.getSchedule().getProgramTitle()); + mDvrManager.updateScheduledRecording( + ScheduledRecording.buildFrom(row.getSchedule()) + .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED) + .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule())) + .build()); + String msg = + mContext.getString( + R.string.dvr_msg_program_scheduled, + row.getSchedule().getProgramTitle()); ToastUtils.show(mContext, msg, Toast.LENGTH_SHORT); } else if (mDvrManager.isConflicting(row.getSchedule())) { mDvrManager.setHighestPriority(row.getSchedule()); @@ -584,9 +640,7 @@ class ScheduleRowPresenter extends RowPresenter { } } - /** - * Action handler for {@link #ACTION_REMOVE_SCHEDULE}. - */ + /** Action handler for {@link #ACTION_REMOVE_SCHEDULE}. */ protected void onRemoveSchedule(ScheduleRow row) { if (row.getSchedule() == null) { // This row has been deleted. @@ -605,13 +659,19 @@ class ScheduleRowPresenter extends RowPresenter { mDvrManager.removeScheduledRecording(row.getSchedule()); } else if (row.isRecordingNotStarted()) { deletedInfo = getDeletedInfo(row); - mDvrManager.updateScheduledRecording(ScheduledRecording.buildFrom(row.getSchedule()) - .setState(ScheduledRecording.STATE_RECORDING_CANCELED) - .build()); + mDvrManager.updateScheduledRecording( + ScheduledRecording.buildFrom(row.getSchedule()) + .setState(ScheduledRecording.STATE_RECORDING_CANCELED) + .build()); + } else if (row.isRecordingFailed()) { + deletedInfo = getDeletedInfo(row); + mDvrManager.removeScheduledRecording(row.getSchedule()); } } if (deletedInfo != null) { - ToastUtils.show(mContext, mContext.getResources() + ToastUtils.show( + mContext, + mContext.getResources() .getString(R.string.dvr_schedules_deletion_info, deletedInfo), Toast.LENGTH_SHORT); } @@ -631,9 +691,7 @@ class ScheduleRowPresenter extends RowPresenter { updateActionContainer(vh, selected); } - /** - * Internal method for onRowViewSelected, can be customized by subclass. - */ + /** Internal method for onRowViewSelected, can be customized by subclass. */ private void updateActionContainer(ViewHolder vh, boolean selected) { ScheduleRowViewHolder viewHolder = (ScheduleRowViewHolder) vh; viewHolder.mSecondActionContainer.animate().setListener(null).cancel(); @@ -643,38 +701,43 @@ class ScheduleRowPresenter extends RowPresenter { case 2: prepareShowActionView(viewHolder.mSecondActionContainer); prepareShowActionView(viewHolder.mFirstActionContainer); - viewHolder.mPendingAnimationRunnable = new Runnable() { - @Override - public void run() { - showActionView(viewHolder.mSecondActionContainer); - showActionView(viewHolder.mFirstActionContainer); - } - }; + viewHolder.mPendingAnimationRunnable = + new Runnable() { + @Override + public void run() { + showActionView(viewHolder.mSecondActionContainer); + showActionView(viewHolder.mFirstActionContainer); + } + }; break; case 1: prepareShowActionView(viewHolder.mFirstActionContainer); - viewHolder.mPendingAnimationRunnable = new Runnable() { - @Override - public void run() { - hideActionView(viewHolder.mSecondActionContainer, View.GONE); - showActionView(viewHolder.mFirstActionContainer); - } - }; + viewHolder.mPendingAnimationRunnable = + new Runnable() { + @Override + public void run() { + hideActionView(viewHolder.mSecondActionContainer, View.GONE); + showActionView(viewHolder.mFirstActionContainer); + } + }; if (mLastFocusedViewId == R.id.action_second_container) { mLastFocusedViewId = R.id.info_container; } break; case 0: default: - viewHolder.mPendingAnimationRunnable = new Runnable() { - @Override - public void run() { - hideActionView(viewHolder.mSecondActionContainer, View.GONE); - hideActionView(viewHolder.mFirstActionContainer, View.GONE); - } - }; + viewHolder.mPendingAnimationRunnable = + new Runnable() { + @Override + public void run() { + hideActionView(viewHolder.mSecondActionContainer, View.GONE); + hideActionView(viewHolder.mFirstActionContainer, View.GONE); + } + }; mLastFocusedViewId = R.id.info_container; - SoftPreconditions.checkState(viewHolder.mInfoContainer.isFocusable(), TAG, + SoftPreconditions.checkState( + viewHolder.mInfoContainer.isFocusable(), + TAG, "No focusable view in this row: " + viewHolder); break; } @@ -685,7 +748,7 @@ class ScheduleRowPresenter extends RowPresenter { // requestFocus() explicitly. if (view.hasFocus()) { viewHolder.mPendingAnimationRunnable.run(); - } else if (view.isFocusable()){ + } else if (view.isFocusable()) { view.requestFocus(); } else { viewHolder.view.requestFocus(); @@ -705,17 +768,16 @@ class ScheduleRowPresenter extends RowPresenter { view.setVisibility(View.VISIBLE); } - /** - * Add animation when view is visible. - */ + /** Add animation when view is visible. */ private void showActionView(View view) { - view.animate().alpha(1.0f).setInterpolator(new DecelerateInterpolator()) - .setDuration(mAnimationDuration).start(); + view.animate() + .alpha(1.0f) + .setInterpolator(new DecelerateInterpolator()) + .setDuration(mAnimationDuration) + .start(); } - /** - * Add animation when view change to invisible. - */ + /** Add animation when view change to invisible. */ private void hideActionView(View view, int visibility) { if (view.getVisibility() != View.VISIBLE) { if (view.getVisibility() != visibility) { @@ -723,15 +785,19 @@ class ScheduleRowPresenter extends RowPresenter { } return; } - view.animate().alpha(0.0f).setInterpolator(new DecelerateInterpolator()) + view.animate() + .alpha(0.0f) + .setInterpolator(new DecelerateInterpolator()) .setDuration(mAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setVisibility(visibility); - view.animate().setListener(null); - } - }).start(); + .setListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(visibility); + view.animate().setListener(null); + } + }) + .start(); } /** @@ -742,8 +808,8 @@ class ScheduleRowPresenter extends RowPresenter { protected int[] getAvailableActions(ScheduleRow row) { if (row.getSchedule() != null) { if (row.isRecordingInProgress()) { - return new int[]{ACTION_STOP_RECORDING}; - } else if (row.isOnAir()) { + return new int[] {ACTION_STOP_RECORDING}; + } else if (row.isOnAir() && !row.hasRecordedProgram()) { if (row.isRecordingNotStarted()) { if (canResolveConflict()) { // The "START" action can change the conflict states. @@ -754,8 +820,12 @@ class ScheduleRowPresenter extends RowPresenter { } else if (row.isRecordingFinished()) { return new int[] {ACTION_START_RECORDING}; } else { - SoftPreconditions.checkState(false, TAG, "Invalid row state in checking the" - + " available actions(on air): " + row); + SoftPreconditions.checkState( + false, + TAG, + "Invalid row state in checking the" + + " available actions(on air): " + + row); } } else { if (row.isScheduleCanceled()) { @@ -764,36 +834,39 @@ class ScheduleRowPresenter extends RowPresenter { return new int[] {ACTION_REMOVE_SCHEDULE, ACTION_CREATE_SCHEDULE}; } else if (row.isRecordingNotStarted()) { return new int[] {ACTION_REMOVE_SCHEDULE}; + } else if (row.isRecordingFailed()) { + return new int[] {ACTION_REMOVE_SCHEDULE}; + } else if (row.isRecordingFinished()) { + return new int[] {}; } else { - SoftPreconditions.checkState(false, TAG, "Invalid row state in checking the" - + " available actions(future schedule): " + row); + SoftPreconditions.checkState( + false, + TAG, + "Invalid row state in checking the" + + " available actions(future schedule): " + + row); } } } return null; } - /** - * Check if the conflict can be resolved in this screen. - */ + /** Check if the conflict can be resolved in this screen. */ protected boolean canResolveConflict() { return true; } - /** - * Check if the schedule should be kept after removing it. - */ + /** Check if the schedule should be kept after removing it. */ protected boolean shouldKeepScheduleAfterRemoving() { return false; } - /** - * Checks if the row should be grayed out. - */ + /** Checks if the row should be grayed out. */ protected boolean shouldBeGrayedOut(ScheduleRow row) { return row.getSchedule() == null - || (row.isOnAir() && !row.isRecordingInProgress()) + || (row.isOnAir() && !row.isRecordingInProgress() && !row.hasRecordedProgram()) || mDvrManager.isConflicting(row.getSchedule()) - || row.isScheduleCanceled(); + || row.isScheduleCanceled() + || row.isRecordingFailed(); } } diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java index 715ecb8c..bbddc07f 100644 --- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java +++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java @@ -18,12 +18,9 @@ package com.android.tv.dvr.ui.list; import com.android.tv.data.Program; import com.android.tv.dvr.data.SeriesRecording; - import java.util.List; -/** - * A base class for the rows for schedules' header. - */ +/** A base class for the rows for schedules' header. */ abstract class SchedulesHeaderRow { private String mTitle; private String mDescription; @@ -35,51 +32,37 @@ abstract class SchedulesHeaderRow { mDescription = description; } - /** - * Sets title. - */ + /** Sets title. */ public void setTitle(String title) { mTitle = title; } - /** - * Sets description. - */ + /** Sets description. */ public void setDescription(String description) { mDescription = description; } - /** - * Sets count of items. - */ + /** Sets count of items. */ public void setItemCount(int itemCount) { mItemCount = itemCount; } - /** - * Returns title. - */ + /** Returns title. */ public String getTitle() { return mTitle; } - /** - * Returns description. - */ + /** Returns description. */ public String getDescription() { return mDescription; } - /** - * Returns count of items. - */ + /** Returns count of items. */ public int getItemCount() { return mItemCount; } - /** - * The header row which represent the date. - */ + /** The header row which represent the date. */ public static class DateHeaderRow extends SchedulesHeaderRow { private long mDeadLineMs; @@ -88,47 +71,41 @@ abstract class SchedulesHeaderRow { mDeadLineMs = deadLineMs; } - /** - * Returns the latest time of the list which belongs to the header row. - */ + /** Returns the latest time of the list which belongs to the header row. */ public long getDeadLineMs() { return mDeadLineMs; } } - /** - * The header row which represent the series recording. - */ + /** The header row which represent the series recording. */ public static class SeriesRecordingHeaderRow extends SchedulesHeaderRow { private SeriesRecording mSeriesRecording; private List<Program> mPrograms; - public SeriesRecordingHeaderRow(String title, String description, int itemCount, - SeriesRecording series, List<Program> programs) { + public SeriesRecordingHeaderRow( + String title, + String description, + int itemCount, + SeriesRecording series, + List<Program> programs) { super(title, description, itemCount); mSeriesRecording = series; mPrograms = programs; } - /** - * Returns the list of programs which belong to the series. - */ + /** Returns the list of programs which belong to the series. */ public List<Program> getPrograms() { return mPrograms; } - /** - * Returns the series recording, it is for series schedules list. - */ + /** Returns the series recording, it is for series schedules list. */ public SeriesRecording getSeriesRecording() { return mSeriesRecording; } - /** - * Sets the series recording. - */ + /** Sets the series recording. */ public void setSeriesRecording(SeriesRecording seriesRecording) { mSeriesRecording = seriesRecording; } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java index fe2033ba..eb01aba2 100644 --- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java @@ -27,16 +27,13 @@ import android.view.View.OnFocusChangeListener; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.TextView; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow; -/** - * A base class for RowPresenter for {@link SchedulesHeaderRow} - */ +/** A base class for RowPresenter for {@link SchedulesHeaderRow} */ abstract class SchedulesHeaderRowPresenter extends RowPresenter { private Context mContext; @@ -46,23 +43,20 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { mContext = context; } - /** - * Returns the context. - */ + /** Returns the context. */ Context getContext() { return mContext; } - /** - * A ViewHolder for {@link SchedulesHeaderRow}. - */ + /** A ViewHolder for {@link SchedulesHeaderRow}. */ public static class SchedulesHeaderRowViewHolder extends RowPresenter.ViewHolder { private TextView mTitle; private TextView mDescription; public SchedulesHeaderRowViewHolder(Context context, ViewGroup parent) { - super(LayoutInflater.from(context).inflate(R.layout.dvr_schedules_header, parent, - false)); + super( + LayoutInflater.from(context) + .inflate(R.layout.dvr_schedules_header, parent, false)); mTitle = (TextView) view.findViewById(R.id.header_title); mDescription = (TextView) view.findViewById(R.id.header_description); } @@ -77,9 +71,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { headerViewHolder.mDescription.setText(header.getDescription()); } - /** - * A presenter for {@link SchedulesHeaderRow.DateHeaderRow}. - */ + /** A presenter for {@link SchedulesHeaderRow.DateHeaderRow}. */ public static class DateHeaderRowPresenter extends SchedulesHeaderRowPresenter { public DateHeaderRowPresenter(Context context) { super(context); @@ -90,10 +82,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { return new DateHeaderRowViewHolder(getContext(), parent); } - /** - * A ViewHolder for - * {@link SchedulesHeaderRow.DateHeaderRow}. - */ + /** A ViewHolder for {@link SchedulesHeaderRow.DateHeaderRow}. */ public static class DateHeaderRowViewHolder extends SchedulesHeaderRowViewHolder { public DateHeaderRowViewHolder(Context context, ViewGroup parent) { super(context, parent); @@ -101,9 +90,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { } } - /** - * A presenter for {@link SeriesRecordingHeaderRow}. - */ + /** A presenter for {@link SeriesRecordingHeaderRow}. */ public static class SeriesRecordingHeaderRowPresenter extends SchedulesHeaderRowPresenter { private final boolean mLtr; private final Drawable mSettingsDrawable; @@ -116,8 +103,9 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { public SeriesRecordingHeaderRowPresenter(Context context) { super(context); - mLtr = context.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mLtr = + context.getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_LTR; mSettingsDrawable = context.getDrawable(R.drawable.ic_settings); mCancelDrawable = context.getDrawable(R.drawable.ic_dvr_cancel_large); mResumeDrawable = context.getDrawable(R.drawable.ic_record_start); @@ -134,8 +122,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { @Override protected void onBindRowViewHolder(RowPresenter.ViewHolder viewHolder, Object item) { super.onBindRowViewHolder(viewHolder, item); - SeriesHeaderRowViewHolder headerViewHolder = - (SeriesHeaderRowViewHolder) viewHolder; + SeriesHeaderRowViewHolder headerViewHolder = (SeriesHeaderRowViewHolder) viewHolder; SeriesRecordingHeaderRow header = (SeriesRecordingHeaderRow) item; headerViewHolder.mSeriesSettingsButton.setVisibility( header.getSeriesRecording().isStopped() ? View.INVISIBLE : View.VISIBLE); @@ -148,46 +135,59 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { headerViewHolder.mToggleStartStopButton.setText(mCancelAllInfo); setTextDrawable(headerViewHolder.mToggleStartStopButton, mCancelDrawable); } - headerViewHolder.mSeriesSettingsButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - DvrUiHelper.startSeriesSettingsActivity(getContext(), - header.getSeriesRecording().getId(), - header.getPrograms(), false, false, false, null); - } - }); - headerViewHolder.mToggleStartStopButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - if (header.getSeriesRecording().isStopped()) { - // Reset priority to the highest. - SeriesRecording seriesRecording = SeriesRecording - .buildFrom(header.getSeriesRecording()) - .setPriority(TvApplication.getSingletons(getContext()) - .getDvrScheduleManager().suggestNewSeriesPriority()) - .build(); - TvApplication.getSingletons(getContext()).getDvrManager() - .updateSeriesRecording(seriesRecording); - DvrUiHelper.startSeriesSettingsActivity(getContext(), - header.getSeriesRecording().getId(), - header.getPrograms(), false, false, false, null); - } else { - DvrUiHelper.showCancelAllSeriesRecordingDialog( - (DvrSchedulesActivity) view.getContext(), - header.getSeriesRecording()); - } - } - }); + headerViewHolder.mSeriesSettingsButton.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View view) { + DvrUiHelper.startSeriesSettingsActivity( + getContext(), + header.getSeriesRecording().getId(), + header.getPrograms(), + false, + false, + false, + null); + } + }); + headerViewHolder.mToggleStartStopButton.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View view) { + if (header.getSeriesRecording().isStopped()) { + // Reset priority to the highest. + SeriesRecording seriesRecording = + SeriesRecording.buildFrom(header.getSeriesRecording()) + .setPriority( + TvSingletons.getSingletons(getContext()) + .getDvrScheduleManager() + .suggestNewSeriesPriority()) + .build(); + TvSingletons.getSingletons(getContext()) + .getDvrManager() + .updateSeriesRecording(seriesRecording); + DvrUiHelper.startSeriesSettingsActivity( + getContext(), + header.getSeriesRecording().getId(), + header.getPrograms(), + false, + false, + false, + null); + } else { + DvrUiHelper.showCancelAllSeriesRecordingDialog( + (DvrSchedulesActivity) view.getContext(), + header.getSeriesRecording()); + } + } + }); } private void setTextDrawable(TextView textView, Drawable drawableStart) { - textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, null, null, - null); + textView.setCompoundDrawablesRelativeWithIntrinsicBounds( + drawableStart, null, null, null); } - /** - * A ViewHolder for {@link SeriesRecordingHeaderRow}. - */ + /** A ViewHolder for {@link SeriesRecordingHeaderRow}. */ public static class SeriesHeaderRowViewHolder extends SchedulesHeaderRowViewHolder { private final TextView mSeriesSettingsButton; private final TextView mToggleStartStopButton; @@ -196,33 +196,40 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { private final View mSelector; private View mLastFocusedView; + public SeriesHeaderRowViewHolder(Context context, ViewGroup parent) { super(context, parent); - mLtr = context.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mLtr = + context.getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_LTR; view.findViewById(R.id.button_container).setVisibility(View.VISIBLE); mSeriesSettingsButton = (TextView) view.findViewById(R.id.series_settings); mToggleStartStopButton = (TextView) view.findViewById(R.id.series_toggle_start_stop); mSelector = view.findViewById(R.id.selector); - OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean focused) { - view.post(new Runnable() { + OnFocusChangeListener onFocusChangeListener = + new View.OnFocusChangeListener() { @Override - public void run() { - updateSelector(view); + public void onFocusChange(View view, boolean focused) { + view.post( + new Runnable() { + @Override + public void run() { + updateSelector(view); + } + }); } - }); - } - }; + }; mSeriesSettingsButton.setOnFocusChangeListener(onFocusChangeListener); mToggleStartStopButton.setOnFocusChangeListener(onFocusChangeListener); } private void updateSelector(View focusedView) { - int animationDuration = mSelector.getContext().getResources() - .getInteger(android.R.integer.config_shortAnimTime); + int animationDuration = + mSelector + .getContext() + .getResources() + .getInteger(android.R.integer.config_shortAnimTime); DecelerateInterpolator interpolator = new DecelerateInterpolator(); if (focusedView.hasFocus()) { @@ -246,21 +253,38 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { // animate the selector in and to the proper width and translation X. final float deltaWidth = lp.width - targetWidth; mSelector.animate().cancel(); - mSelector.animate().translationX(targetTranslationX).alpha(1f) - .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - // Set width to the proper width for this animation step. - lp.width = targetWidth + Math.round( - deltaWidth * (1f - animation.getAnimatedFraction())); - mSelector.requestLayout(); - } - }).setDuration(animationDuration).setInterpolator(interpolator).start(); + mSelector + .animate() + .translationX(targetTranslationX) + .alpha(1f) + .setUpdateListener( + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // Set width to the proper width for this animation + // step. + lp.width = + targetWidth + + Math.round( + deltaWidth + * (1f + - animation + .getAnimatedFraction())); + mSelector.requestLayout(); + } + }) + .setDuration(animationDuration) + .setInterpolator(interpolator) + .start(); mLastFocusedView = focusedView; } else if (mLastFocusedView == focusedView) { mSelector.animate().setUpdateListener(null).cancel(); - mSelector.animate().alpha(0f).setDuration(animationDuration) - .setInterpolator(interpolator).start(); + mSelector + .animate() + .alpha(0f) + .setDuration(animationDuration) + .setInterpolator(interpolator) + .start(); mLastFocusedView = null; } } diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java index 6b6de8b8..9a9c94ea 100644 --- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java +++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2016 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 -*/ + * Copyright (C) 2016 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.list; @@ -23,10 +23,8 @@ import android.os.Build; import android.support.v17.leanback.widget.ClassPresenterSelector; import android.util.ArrayMap; import android.util.Log; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Program; import com.android.tv.dvr.DvrDataManager; @@ -35,16 +33,13 @@ import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.dvr.data.SeriesRecording; import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow; import com.android.tv.util.Utils; - import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; -/** - * An adapter for series schedule row. - */ +/** An adapter for series schedule row. */ @TargetApi(Build.VERSION_CODES.N) class SeriesScheduleRowAdapter extends ScheduleRowAdapter { private static final String TAG = "SeriesRowAdapter"; @@ -57,7 +52,9 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { private final Map<Long, Program> mPrograms = new ArrayMap<>(); private SeriesRecordingHeaderRow mHeaderRow; - public SeriesScheduleRowAdapter(Context context, ClassPresenterSelector classPresenterSelector, + public SeriesScheduleRowAdapter( + Context context, + ClassPresenterSelector classPresenterSelector, SeriesRecording seriesRecording) { super(context, classPresenterSelector); mSeriesRecording = seriesRecording; @@ -67,7 +64,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { } else { mInputId = null; } - ApplicationSingletons singletons = TvApplication.getSingletons(context); + TvSingletons singletons = TvSingletons.getSingletons(context); mDvrManager = singletons.getDvrManager(); mDataManager = singletons.getDvrDataManager(); setHasStableIds(true); @@ -83,9 +80,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { super.stop(); } - /** - * Sets the programs to show. - */ + /** Sets the programs to show. */ public void setPrograms(List<Program> programs) { if (programs == null) { programs = Collections.emptyList(); @@ -95,8 +90,13 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { List<Program> sortedPrograms = new ArrayList<>(programs); Collections.sort(sortedPrograms); List<EpisodicProgramRow> rows = new ArrayList<>(); - mHeaderRow = new SeriesRecordingHeaderRow(mSeriesRecording.getTitle(), - null, sortedPrograms.size(), mSeriesRecording, programs); + mHeaderRow = + new SeriesRecordingHeaderRow( + mSeriesRecording.getTitle(), + null, + sortedPrograms.size(), + mSeriesRecording, + programs); for (Program program : sortedPrograms) { ScheduledRecording schedule = mDataManager.getScheduledRecordingForProgramId(program.getId()); @@ -122,8 +122,14 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { ++conflicts; } } - return conflicts == 0 ? null : getContext().getResources().getQuantityString( - R.plurals.dvr_series_schedules_header_description, conflicts, conflicts); + return conflicts == 0 + ? null + : getContext() + .getResources() + .getQuantityString( + R.plurals.dvr_series_schedules_header_description, + conflicts, + conflicts); } @Override diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java index c8503e0d..263c579e 100644 --- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java @@ -1,33 +1,30 @@ /* -* Copyright (C) 2016 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 -*/ + * Copyright (C) 2016 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.list; import android.content.Context; import android.view.View; import android.view.ViewGroup; - import com.android.tv.R; import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.ui.DvrUiHelper; import com.android.tv.util.Utils; -/** - * A RowPresenter for series schedule row. - */ +/** A RowPresenter for series schedule row. */ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { private static final String TAG = "SeriesRowPresenter"; @@ -35,16 +32,18 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { public SeriesScheduleRowPresenter(Context context) { super(context); - mLtr = context.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mLtr = + context.getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_LTR; } public static class SeriesScheduleRowViewHolder extends ScheduleRowViewHolder { public SeriesScheduleRowViewHolder(View view, ScheduleRowPresenter presenter) { super(view, presenter); ViewGroup.LayoutParams lp = getTimeView().getLayoutParams(); - lp.width = view.getResources().getDimensionPixelSize( - R.dimen.dvr_series_schedules_item_time_width); + lp.width = + view.getResources() + .getDimensionPixelSize(R.dimen.dvr_series_schedules_item_time_width); getTimeView().setLayoutParams(lp); } } @@ -56,8 +55,8 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { @Override protected String onGetRecordingTimeText(ScheduleRow row) { - return Utils.getDurationString(getContext(), row.getStartTimeMs(), row.getEndTimeMs(), - false, true, true, 0); + return Utils.getDurationString( + getContext(), row.getStartTimeMs(), row.getEndTimeMs(), false, true, true, 0); } @Override @@ -71,11 +70,17 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { SeriesScheduleRowViewHolder viewHolder = (SeriesScheduleRowViewHolder) vh; EpisodicProgramRow row = (EpisodicProgramRow) item; if (getDvrManager().isConflicting(row.getSchedule())) { - viewHolder.getProgramTitleView().setCompoundDrawablePadding(getContext() - .getResources().getDimensionPixelOffset( - R.dimen.dvr_schedules_warning_icon_padding)); - viewHolder.getProgramTitleView().setCompoundDrawablesRelativeWithIntrinsicBounds( - R.drawable.ic_warning_gray600_36dp, 0, 0, 0); + viewHolder + .getProgramTitleView() + .setCompoundDrawablePadding( + getContext() + .getResources() + .getDimensionPixelOffset( + R.dimen.dvr_schedules_warning_icon_padding)); + viewHolder + .getProgramTitleView() + .setCompoundDrawablesRelativeWithIntrinsicBounds( + R.drawable.ic_warning_gray600_36dp, 0, 0, 0); } else { viewHolder.getProgramTitleView().setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } @@ -88,16 +93,16 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { @Override protected void onStartRecording(ScheduleRow row) { - SoftPreconditions.checkState(row.getSchedule() == null, TAG, - "Start request with the existing schedule: " + row); + SoftPreconditions.checkState( + row.getSchedule() == null, TAG, "Start request with the existing schedule: " + row); row.setStartRecordingRequested(true); getDvrManager().addScheduleWithHighestPriority(((EpisodicProgramRow) row).getProgram()); } @Override protected void onStopRecording(ScheduleRow row) { - SoftPreconditions.checkState(row.getSchedule() != null, TAG, - "Stop request with the null schedule: " + row); + SoftPreconditions.checkState( + row.getSchedule() != null, TAG, "Stop request with the null schedule: " + row); row.setStopRecordingRequested(true); getDvrManager().stopRecording(row.getSchedule()); } diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java index 6824cfe2..b8b19adc 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java @@ -23,16 +23,13 @@ import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.util.Log; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.Starter; import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener; import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.util.Utils; -/** - * Activity to play a {@link RecordedProgram}. - */ +/** Activity to play a {@link RecordedProgram}. */ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListener { private static final String TAG = "DvrPlaybackActivity"; private static final boolean DEBUG = false; @@ -42,13 +39,14 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene @Override public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); + Starter.start(this); if (DEBUG) Log.d(TAG, "onCreate"); super.onCreate(savedInstanceState); setIntent(createProgramIntent(getIntent())); setContentView(R.layout.activity_dvr_playback); - mOverlayFragment = (DvrPlaybackOverlayFragment) getFragmentManager() - .findFragmentById(R.id.dvr_playback_controls_fragment); + mOverlayFragment = + (DvrPlaybackOverlayFragment) + getFragmentManager().findFragmentById(R.id.dvr_playback_controls_fragment); } @Override @@ -68,7 +66,8 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); float density = getResources().getDisplayMetrics().density; - mOverlayFragment.onWindowSizeChanged((int) (newConfig.screenWidthDp * density), + mOverlayFragment.onWindowSizeChanged( + (int) (newConfig.screenWidthDp * density), (int) (newConfig.screenHeightDp * density)); } @@ -91,4 +90,4 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene void setOnPinCheckListener(OnPinCheckedListener listener) { mOnPinCheckedListener = listener; } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java index 8ef0041d..a6352d95 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java @@ -17,14 +17,11 @@ package com.android.tv.dvr.ui.playback; import android.content.Context; - import com.android.tv.R; import com.android.tv.dvr.ui.browse.RecordedProgramPresenter; import com.android.tv.dvr.ui.browse.RecordingCardView; -/** - * This class is used to generate Views and bind Objects for related recordings in DVR playback. - */ +/** This class is used to generate Views and bind Objects for related recordings in DVR playback. */ class DvrPlaybackCardPresenter extends RecordedProgramPresenter { private final int mRelatedRecordingCardWidth; private final int mRelatedRecordingCardHeight; @@ -39,7 +36,12 @@ class DvrPlaybackCardPresenter extends RecordedProgramPresenter { @Override public DvrItemViewHolder onCreateDvrItemViewHolder() { - return new RecordedProgramViewHolder(new RecordingCardView( - getContext(), mRelatedRecordingCardWidth, mRelatedRecordingCardHeight, true), null); + return new RecordedProgramViewHolder( + new RecordingCardView( + getContext(), + mRelatedRecordingCardWidth, + mRelatedRecordingCardHeight, + true), + null); } } diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java index 1a6ae187..59c90d11 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java @@ -44,8 +44,8 @@ import com.android.tv.util.TimeShiftUtils; import java.util.ArrayList; /** - * A helper class to assist {@link DvrPlaybackOverlayFragment} to manage its controls row and - * send command to the media controller. It also helps to update playback states displayed in the + * A helper class to assist {@link DvrPlaybackOverlayFragment} to manage its controls row and send + * command to the media controller. It also helps to update playback states displayed in the * fragment according to information the media session provides. */ class DvrPlaybackControlHelper extends PlaybackControlGlue { @@ -74,8 +74,9 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { mMediaController = activity.getMediaController(); mMediaController.registerCallback(mMediaControllerCallback); mTransportControls = mMediaController.getTransportControls(); - mExtraPaddingTopForNoDescription = activity.getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top); + mExtraPaddingTopForNoDescription = + activity.getResources() + .getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top); mClosedCaptioningAction = new ClosedCaptioningAction(activity); mMultiAudioAction = new MultiAudioAction(activity); createControlsRowPresenter(); @@ -90,42 +91,47 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { private void createControlsRowPresenter() { AbstractDetailsDescriptionPresenter detailsPresenter = new AbstractDetailsDescriptionPresenter() { - @Override - protected void onBindDescription( - AbstractDetailsDescriptionPresenter.ViewHolder viewHolder, Object object) { - PlaybackControlGlue glue = (PlaybackControlGlue) object; - if (glue.hasValidMedia()) { - viewHolder.getTitle().setText(glue.getMediaTitle()); - viewHolder.getSubtitle().setText(glue.getMediaSubtitle()); - } else { - viewHolder.getTitle().setText(""); - viewHolder.getSubtitle().setText(""); - } - if (TextUtils.isEmpty(viewHolder.getSubtitle().getText())) { - viewHolder.view.setPadding(viewHolder.view.getPaddingLeft(), - mExtraPaddingTopForNoDescription, - viewHolder.view.getPaddingRight(), viewHolder.view.getPaddingBottom()); - } - } - }; + @Override + protected void onBindDescription( + AbstractDetailsDescriptionPresenter.ViewHolder viewHolder, + Object object) { + PlaybackControlGlue glue = (PlaybackControlGlue) object; + if (glue.hasValidMedia()) { + viewHolder.getTitle().setText(glue.getMediaTitle()); + viewHolder.getSubtitle().setText(glue.getMediaSubtitle()); + } else { + viewHolder.getTitle().setText(""); + viewHolder.getSubtitle().setText(""); + } + if (TextUtils.isEmpty(viewHolder.getSubtitle().getText())) { + viewHolder.view.setPadding( + viewHolder.view.getPaddingLeft(), + mExtraPaddingTopForNoDescription, + viewHolder.view.getPaddingRight(), + viewHolder.view.getPaddingBottom()); + } + } + }; PlaybackControlsRowPresenter presenter = new PlaybackControlsRowPresenter(detailsPresenter) { - @Override - protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) { - super.onBindRowViewHolder(vh, item); - vh.setOnKeyListener(DvrPlaybackControlHelper.this); - } - - @Override - protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) { - super.onUnbindRowViewHolder(vh); - vh.setOnKeyListener(null); - } - }; - presenter.setProgressColor(getContext().getResources() - .getColor(R.color.play_controls_progress_bar_watched)); - presenter.setBackgroundColor(getContext().getResources() - .getColor(R.color.play_controls_body_background_enabled)); + @Override + protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) { + super.onBindRowViewHolder(vh, item); + vh.setOnKeyListener(DvrPlaybackControlHelper.this); + } + + @Override + protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) { + super.onUnbindRowViewHolder(vh); + vh.setOnKeyListener(null); + } + }; + presenter.setProgressColor( + getContext().getResources().getColor(R.color.play_controls_progress_bar_watched)); + presenter.setBackgroundColor( + getContext() + .getResources() + .getColor(R.color.play_controls_body_background_enabled)); setControlsRowPresenter(presenter); } @@ -166,37 +172,40 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { return false; } int state = playbackState.getState(); - return state != PlaybackState.STATE_NONE && state != PlaybackState.STATE_CONNECTING + return state != PlaybackState.STATE_NONE + && state != PlaybackState.STATE_CONNECTING && state != PlaybackState.STATE_PAUSED; } - /** - * Returns the ID of the media under playback. - */ + /** Returns the ID of the media under playback. */ public String getMediaId() { MediaMetadata mediaMetadata = mMediaController.getMetadata(); - return mediaMetadata == null ? null + return mediaMetadata == null + ? null : mediaMetadata.getString(MediaMetadata.METADATA_KEY_MEDIA_ID); } @Override public CharSequence getMediaTitle() { MediaMetadata mediaMetadata = mMediaController.getMetadata(); - return mediaMetadata == null ? "" + return mediaMetadata == null + ? "" : mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); } @Override public CharSequence getMediaSubtitle() { MediaMetadata mediaMetadata = mMediaController.getMetadata(); - return mediaMetadata == null ? "" + return mediaMetadata == null + ? "" : mediaMetadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE); } @Override public int getMediaDuration() { MediaMetadata mediaMetadata = mMediaController.getMetadata(); - return mediaMetadata == null ? 0 + return mediaMetadata == null + ? 0 : (int) mediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); } @@ -225,19 +234,18 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { return (int) playbackState.getPosition(); } - /** - * Unregister media controller's callback. - */ + /** Unregister media controller's callback. */ void unregisterCallback() { mMediaController.unregisterCallback(mMediaControllerCallback); } /** * Update the secondary controls row. - * @param hasClosedCaption {@code true} to show the closed caption selection button, - * {@code false} to hide it. - * @param hasMultiAudio {@code true} to show the audio track selection button, - * {@code false} to hide it. + * + * @param hasClosedCaption {@code true} to show the closed caption selection button, {@code + * false} to hide it. + * @param hasMultiAudio {@code true} to show the audio track selection button, {@code false} to + * hide it. */ void updateSecondaryRow(boolean hasClosedCaption, boolean hasMultiAudio) { if (hasClosedCaption) { @@ -274,7 +282,7 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { mTransportControls.play(); } else if (speedId <= -PLAYBACK_SPEED_FAST_L0) { mTransportControls.rewind(); - } else if (speedId >= PLAYBACK_SPEED_FAST_L0){ + } else if (speedId >= PLAYBACK_SPEED_FAST_L0) { mTransportControls.fastForward(); } } @@ -284,12 +292,10 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { mTransportControls.pause(); } - /** - * Notifies closed caption being enabled/disabled to update related UI. - */ + /** Notifies closed caption being enabled/disabled to update related UI. */ void onSubtitleTrackStateChanged(boolean enabled) { - mClosedCaptioningAction.setIndex(enabled ? - ClosedCaptioningAction.ON : ClosedCaptioningAction.OFF); + mClosedCaptioningAction.setIndex( + enabled ? ClosedCaptioningAction.ON : ClosedCaptioningAction.OFF); } private void onStateChanged(int state, long positionMs, int speedLevel) { @@ -344,7 +350,9 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { args.putString(DvrPlaybackSideFragment.SELECTED_TRACK_ID, selectedTrackId); DvrPlaybackSideFragment sideFragment = new DvrPlaybackSideFragment(); sideFragment.setArguments(args); - mFragment.getFragmentManager().beginTransaction() + mFragment + .getFragmentManager() + .beginTransaction() .hide(mFragment) .replace(R.id.dvr_playback_side_fragment, sideFragment) .addToBackStack(null) @@ -367,7 +375,7 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { private static class MultiAudioAction extends MultiAction { MultiAudioAction(Context context) { super(AUDIO_ACTION_ID); - setDrawables(new Drawable[]{context.getDrawable(R.drawable.ic_tvoption_multi_track)}); + setDrawables(new Drawable[] {context.getDrawable(R.drawable.ic_tvoption_multi_track)}); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java index 843d2dbe..bef036eb 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java @@ -28,17 +28,15 @@ import android.media.tv.TvContract; import android.os.AsyncTask; import android.support.annotation.Nullable; import android.text.TextUtils; - import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.TvSingletons; import com.android.tv.data.ChannelDataManager; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.DvrWatchedPositionManager; import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.util.ImageLoader; import com.android.tv.util.TimeShiftUtils; import com.android.tv.util.Utils; +import com.android.tv.util.images.ImageLoader; class DvrPlaybackMediaSessionHelper { private static final String TAG = "DvrPlaybackMediaSessionHelper"; @@ -55,49 +53,52 @@ class DvrPlaybackMediaSessionHelper { private final DvrWatchedPositionManager mDvrWatchedPositionManager; private final ChannelDataManager mChannelDataManager; - public DvrPlaybackMediaSessionHelper(Activity activity, String mediaSessionTag, - DvrPlayer dvrPlayer, DvrPlaybackOverlayFragment overlayFragment) { + public DvrPlaybackMediaSessionHelper( + Activity activity, + String mediaSessionTag, + DvrPlayer dvrPlayer, + DvrPlaybackOverlayFragment overlayFragment) { mActivity = activity; mDvrPlayer = dvrPlayer; mDvrWatchedPositionManager = - TvApplication.getSingletons(activity).getDvrWatchedPositionManager(); - mChannelDataManager = TvApplication.getSingletons(activity).getChannelDataManager(); - mDvrPlayer.setCallback(new DvrPlayer.DvrPlayerCallback() { - @Override - public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { - updateMediaSessionPlaybackState(); - } + TvSingletons.getSingletons(activity).getDvrWatchedPositionManager(); + mChannelDataManager = TvSingletons.getSingletons(activity).getChannelDataManager(); + mDvrPlayer.setCallback( + new DvrPlayer.DvrPlayerCallback() { + @Override + public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { + updateMediaSessionPlaybackState(); + } - @Override - public void onPlaybackPositionChanged(long positionMs) { - updateMediaSessionPlaybackState(); - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrWatchedPositionManager - .setWatchedPosition(mDvrPlayer.getProgram().getId(), positionMs); - } - } + @Override + public void onPlaybackPositionChanged(long positionMs) { + updateMediaSessionPlaybackState(); + if (mDvrPlayer.isPlaybackPrepared()) { + mDvrWatchedPositionManager.setWatchedPosition( + mDvrPlayer.getProgram().getId(), positionMs); + } + } - @Override - public void onPlaybackEnded() { - // TODO: Deal with watched over recordings in DVR library - RecordedProgram nextEpisode = - overlayFragment.getNextEpisode(mDvrPlayer.getProgram()); - if (nextEpisode == null) { - mDvrPlayer.reset(); - mActivity.finish(); - } else { - Intent intent = new Intent(activity, DvrPlaybackActivity.class); - intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, nextEpisode.getId()); - mActivity.startActivity(intent); - } - } - }); + @Override + public void onPlaybackEnded() { + // TODO: Deal with watched over recordings in DVR library + RecordedProgram nextEpisode = + overlayFragment.getNextEpisode(mDvrPlayer.getProgram()); + if (nextEpisode == null) { + mDvrPlayer.reset(); + mActivity.finish(); + } else { + Intent intent = new Intent(activity, DvrPlaybackActivity.class); + intent.putExtra( + Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, nextEpisode.getId()); + mActivity.startActivity(intent); + } + } + }); initializeMediaSession(mediaSessionTag); } - /** - * Stops DVR player and release media session. - */ + /** Stops DVR player and release media session. */ public void release() { if (mDvrPlayer != null) { mDvrPlayer.reset(); @@ -108,13 +109,15 @@ class DvrPlaybackMediaSessionHelper { } } - /** - * Updates media session's playback state and speed. - */ + /** Updates media session's playback state and speed. */ public void updateMediaSessionPlaybackState() { - mMediaSession.setPlaybackState(new PlaybackState.Builder() - .setState(mDvrPlayer.getPlaybackState(), mDvrPlayer.getPlaybackPosition(), - mSpeedLevel).build()); + mMediaSession.setPlaybackState( + new PlaybackState.Builder() + .setState( + mDvrPlayer.getPlaybackState(), + mDvrPlayer.getPlaybackPosition(), + mSpeedLevel) + .build()); } /** @@ -132,42 +135,35 @@ class DvrPlaybackMediaSessionHelper { } } - /** - * Returns the recorded program now playing. - */ + /** Returns the recorded program now playing. */ public RecordedProgram getProgram() { return mDvrPlayer.getProgram(); } - /** - * Checks if the recorded program is the same as now playing one. - */ + /** Checks if the recorded program is the same as now playing one. */ public boolean isCurrentProgram(RecordedProgram program) { return program != null && program.equals(getProgram()); } - /** - * Returns playback state. - */ + /** Returns playback state. */ public int getPlaybackState() { return mDvrPlayer.getPlaybackState(); } - /** - * Returns the underlying DVR player. - */ + /** Returns the underlying DVR player. */ public DvrPlayer getDvrPlayer() { return mDvrPlayer; } private void initializeMediaSession(String mediaSessionTag) { mMediaSession = new MediaSession(mActivity, mediaSessionTag); - mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS - | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); - mNowPlayingCardWidth = mActivity.getResources() - .getDimensionPixelSize(R.dimen.notif_card_img_max_width); - mNowPlayingCardHeight = mActivity.getResources() - .getDimensionPixelSize(R.dimen.notif_card_img_height); + mMediaSession.setFlags( + MediaSession.FLAG_HANDLES_MEDIA_BUTTONS + | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); + mNowPlayingCardWidth = + mActivity.getResources().getDimensionPixelSize(R.dimen.notif_card_img_max_width); + mNowPlayingCardHeight = + mActivity.getResources().getDimensionPixelSize(R.dimen.notif_card_img_height); mMediaSession.setCallback(new MediaSessionCallback()); mActivity.setMediaController( new MediaController(mActivity, mMediaSession.getSessionToken())); @@ -179,11 +175,17 @@ class DvrPlaybackMediaSessionHelper { String cardTitleText = program.getTitle(); if (TextUtils.isEmpty(cardTitleText)) { Channel channel = mChannelDataManager.getChannel(program.getChannelId()); - cardTitleText = (channel != null) ? channel.getDisplayName() - : mActivity.getString(R.string.no_program_information); + cardTitleText = + (channel != null) + ? channel.getDisplayName() + : mActivity.getString(R.string.no_program_information); } - final MediaMetadata currentMetadata = updateMetadataTextInfo(program.getId(), cardTitleText, - program.getDescription(), mProgramDurationMs); + final MediaMetadata currentMetadata = + updateMetadataTextInfo( + program.getId(), + cardTitleText, + program.getDescription(), + mProgramDurationMs); String posterArtUri = program.getPosterArtUri(); if (posterArtUri == null) { posterArtUri = TvContract.buildChannelLogoUri(program.getChannelId()).toString(); @@ -192,12 +194,18 @@ class DvrPlaybackMediaSessionHelper { mMediaSession.setActive(true); } - private void updatePosterArt(RecordedProgram program, MediaMetadata currentMetadata, - @Nullable Bitmap posterArt, @Nullable String posterArtUri) { + private void updatePosterArt( + RecordedProgram program, + MediaMetadata currentMetadata, + @Nullable Bitmap posterArt, + @Nullable String posterArtUri) { if (posterArt != null) { updateMetadataImageInfo(program, currentMetadata, posterArt, 0); } else if (posterArtUri != null) { - ImageLoader.loadBitmap(mActivity, posterArtUri, mNowPlayingCardWidth, + ImageLoader.loadBitmap( + mActivity, + posterArtUri, + mNowPlayingCardWidth, mNowPlayingCardHeight, new ProgramPosterArtCallback(mActivity, program, currentMetadata)); } else { @@ -205,13 +213,12 @@ class DvrPlaybackMediaSessionHelper { } } - private class ProgramPosterArtCallback extends - ImageLoader.ImageLoaderCallback<Activity> { + private class ProgramPosterArtCallback extends ImageLoader.ImageLoaderCallback<Activity> { private final RecordedProgram mRecordedProgram; private final MediaMetadata mCurrentMetadata; - public ProgramPosterArtCallback(Activity activity, RecordedProgram program, - MediaMetadata metadata) { + public ProgramPosterArtCallback( + Activity activity, RecordedProgram program, MediaMetadata metadata) { super(activity); mRecordedProgram = program; mCurrentMetadata = metadata; @@ -225,8 +232,8 @@ class DvrPlaybackMediaSessionHelper { } } - private MediaMetadata updateMetadataTextInfo(final long programId, final String title, - final String subtitle, final long duration) { + private MediaMetadata updateMetadataTextInfo( + final long programId, final String title, final String subtitle, final long duration) { MediaMetadata.Builder builder = new MediaMetadata.Builder(); builder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, Long.toString(programId)) .putString(MediaMetadata.METADATA_KEY_TITLE, title) @@ -239,8 +246,11 @@ class DvrPlaybackMediaSessionHelper { return metadata; } - private void updateMetadataImageInfo(final RecordedProgram program, - final MediaMetadata currentMetadata, final Bitmap posterArt, final int imageResId) { + private void updateMetadataImageInfo( + final RecordedProgram program, + final MediaMetadata currentMetadata, + final Bitmap posterArt, + final int imageResId) { if (mMediaSession != null && (posterArt != null || imageResId != 0)) { MediaMetadata.Builder builder = new MediaMetadata.Builder(currentMetadata); if (posterArt != null) { @@ -255,7 +265,8 @@ class DvrPlaybackMediaSessionHelper { @Override protected void onPostExecute(Bitmap programPosterArt) { - if (mMediaSession != null && programPosterArt != null + if (mMediaSession != null + && programPosterArt != null && isCurrentProgram(program)) { builder.putBitmap(MediaMetadata.METADATA_KEY_ART, programPosterArt); mMediaSession.setMetadata(builder.build()); diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java index 783ae682..d3374cfa 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java @@ -21,12 +21,12 @@ import android.content.Context; import android.content.Intent; import android.graphics.Point; import android.hardware.display.DisplayManager; -import android.media.tv.TvContentRating; -import android.media.tv.TvTrackInfo; -import android.os.Bundle; import android.media.session.PlaybackState; +import android.media.tv.TvContentRating; import android.media.tv.TvInputManager; +import android.media.tv.TvTrackInfo; import android.media.tv.TvView; +import android.os.Bundle; import android.support.v17.leanback.app.PlaybackFragment; import android.support.v17.leanback.app.PlaybackFragmentGlueHost; import android.support.v17.leanback.widget.ArrayObjectAdapter; @@ -37,14 +37,13 @@ import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.RowPresenter; import android.support.v17.leanback.widget.SinglePresenterSelector; +import android.util.Log; import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; -import android.util.Log; - import com.android.tv.R; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; import com.android.tv.data.BaseProgram; import com.android.tv.dialog.PinDialogFragment; import com.android.tv.dvr.DvrDataManager; @@ -57,9 +56,8 @@ import com.android.tv.parental.ContentRatingsManager; import com.android.tv.util.TvSettings; import com.android.tv.util.TvTrackInfoUtils; import com.android.tv.util.Utils; - -import java.util.List; import java.util.ArrayList; +import java.util.List; public class DvrPlaybackOverlayFragment extends PlaybackFragment { // TODO: Handles audio focus. Deals with block and ratings. @@ -104,15 +102,25 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate"); super.onCreate(savedInstanceState); - mVerticalPaddingBase = getActivity().getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_base); - mPaddingWithoutRelatedRow = getActivity().getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_related_row); - mPaddingWithoutSecondaryRow = getActivity().getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_secondary_row); - mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager(); - mContentRatingsManager = TvApplication.getSingletons(getContext()) - .getTvInputManagerHelper().getContentRatingsManager(); + mVerticalPaddingBase = + getActivity() + .getResources() + .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_base); + mPaddingWithoutRelatedRow = + getActivity() + .getResources() + .getDimensionPixelOffset( + R.dimen.dvr_playback_overlay_padding_top_no_related_row); + mPaddingWithoutSecondaryRow = + getActivity() + .getResources() + .getDimensionPixelOffset( + R.dimen.dvr_playback_overlay_padding_top_no_secondary_row); + mDvrDataManager = TvSingletons.getSingletons(getActivity()).getDvrDataManager(); + mContentRatingsManager = + TvSingletons.getSingletons(getContext()) + .getTvInputManagerHelper() + .getContentRatingsManager(); if (!mDvrDataManager.isRecordedProgramLoadFinished()) { mDvrDataManager.addRecordedProgramLoadFinishedListener( new DvrDataManager.OnRecordedProgramLoadFinishedListener() { @@ -124,14 +132,14 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { preparePlayback(getActivity().getIntent()); } } - } - ); + }); } else if (!handleIntent(getActivity().getIntent(), true)) { return; } Point size = new Point(); ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE)) - .getDisplay(Display.DEFAULT_DISPLAY).getSize(size); + .getDisplay(Display.DEFAULT_DISPLAY) + .getSize(size); mWindowWidth = size.x; mWindowHeight = size.y; mWindowAspectRatio = mAppliedAspectRatio = (float) mWindowWidth / mWindowHeight; @@ -152,19 +160,20 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { mTvView = (TvView) getActivity().findViewById(R.id.dvr_tv_view); mBlockScreenView = getActivity().findViewById(R.id.block_screen); mDvrPlayer = new DvrPlayer(mTvView); - mMediaSessionHelper = new DvrPlaybackMediaSessionHelper( - getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this); + mMediaSessionHelper = + new DvrPlaybackMediaSessionHelper( + getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this); mPlaybackControlHelper = new DvrPlaybackControlHelper(getActivity(), this); mRelatedRecordingsRow = getRelatedRecordingsRow(); mDvrPlayer.setOnTracksAvailabilityChangedListener( new DvrPlayer.OnTracksAvailabilityChangedListener() { @Override - public void onTracksAvailabilityChanged(boolean hasClosedCaption, - boolean hasMultiAudio) { + public void onTracksAvailabilityChanged( + boolean hasClosedCaption, boolean hasMultiAudio) { mPlaybackControlHelper.updateSecondaryRow(hasClosedCaption, hasMultiAudio); if (hasClosedCaption) { - mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE, - mOnSubtitleTrackSelectedListener); + mDvrPlayer.setOnTrackSelectedListener( + TvTrackInfo.TYPE_SUBTITLE, mOnSubtitleTrackSelectedListener); selectBestMatchedTrack(TvTrackInfo.TYPE_SUBTITLE); } else { mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE, null); @@ -175,15 +184,18 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { updateVerticalPosition(); mPlaybackControlHelper.getHost().notifyPlaybackRowChanged(); } - }); - mDvrPlayer.setOnAspectRatioChangedListener(new DvrPlayer.OnAspectRatioChangedListener() { - @Override - public void onAspectRatioChanged(float videoAspectRatio) { - updateAspectRatio(videoAspectRatio); - } - }); - mPinChecked = getActivity().getIntent() - .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false); + }); + mDvrPlayer.setOnAspectRatioChangedListener( + new DvrPlayer.OnAspectRatioChangedListener() { + @Override + public void onAspectRatioChanged(float videoAspectRatio) { + updateAspectRatio(videoAspectRatio); + } + }); + mPinChecked = + getActivity() + .getIntent() + .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false); mDvrPlayer.setOnContentBlockedListener( new DvrPlayer.OnContentBlockedListener() { @Override @@ -221,20 +233,25 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { PinDialogFragment.DIALOG_TAG); } }); - setOnItemViewClickedListener(new BaseOnItemViewClickedListener() { - @Override - public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, - RowPresenter.ViewHolder rowViewHolder, Object row) { - if (itemViewHolder.view instanceof RecordingCardView) { - setFadingEnabled(false); - long programId = ((RecordedProgram) itemViewHolder.view.getTag()).getId(); - if (DEBUG) Log.d(TAG, "Play Related Recording:" + programId); - Intent intent = new Intent(getContext(), DvrPlaybackActivity.class); - intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId); - getContext().startActivity(intent); - } - } - }); + setOnItemViewClickedListener( + new BaseOnItemViewClickedListener() { + @Override + public void onItemClicked( + Presenter.ViewHolder itemViewHolder, + Object item, + RowPresenter.ViewHolder rowViewHolder, + Object row) { + if (itemViewHolder.view instanceof RecordingCardView) { + setFadingEnabled(false); + long programId = + ((RecordedProgram) itemViewHolder.view.getTag()).getId(); + if (DEBUG) Log.d(TAG, "Play Related Recording:" + programId); + Intent intent = new Intent(getContext(), DvrPlaybackActivity.class); + intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId); + getContext().startActivity(intent); + } + } + }); if (mProgram != null) { setUpRows(); preparePlayback(getActivity().getIntent()); @@ -265,9 +282,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { super.onDestroy(); } - /** - * Passes the intent to the fragment. - */ + /** Passes the intent to the fragment. */ public void onNewIntent(Intent intent) { if (mDvrDataManager.isRecordedProgramLoadFinished() && handleIntent(intent, false)) { preparePlayback(intent); @@ -275,8 +290,8 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { } /** - * Should be called when windows' size is changed in order to notify DVR player - * to update it's view width/height and position. + * Should be called when windows' size is changed in order to notify DVR player to update it's + * view width/height and position. */ public void onWindowSizeChanged(final int windowWidth, final int windowHeight) { mWindowWidth = windowWidth; @@ -285,9 +300,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { updateAspectRatio(mAppliedAspectRatio); } - /** - * Returns next recorded episode in the same series as now playing program. - */ + /** Returns next recorded episode in the same series as now playing program. */ public RecordedProgram getNextEpisode(RecordedProgram program) { int position = mRelatedRecordingsRowAdapter.findInsertPosition(program); if (position == mRelatedRecordingsRowAdapter.size()) { @@ -299,9 +312,9 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { /** * Returns the tracks of the give type of the current playback. - - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. Or returns {@code null}. + * + * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link + * TvTrackInfo#TYPE_AUDIO}. Or returns {@code null}. */ public ArrayList<TvTrackInfo> getTracks(int trackType) { if (trackType == TvTrackInfo.TYPE_AUDIO) { @@ -312,18 +325,16 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { return null; } - /** - * Returns the ID of the selected track of the given type. - */ + /** Returns the ID of the selected track of the given type. */ public String getSelectedTrackId(int trackType) { return mDvrPlayer.getSelectedTrackId(trackType); } /** * Returns the language setting of the given track type. - - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. + * + * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link + * TvTrackInfo#TYPE_AUDIO}. * @return {@code null} if no language has been set for the given track type. */ TvTrackInfo getTrackSetting(int trackType) { @@ -332,10 +343,11 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { /** * Selects the given audio or subtitle track for DVR playback. - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. + * + * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link + * TvTrackInfo#TYPE_AUDIO}. * @param selectedTrack {@code null} to disable the audio or subtitle track according to - * trackType. + * trackType. */ void selectTrack(int trackType, TvTrackInfo selectedTrack) { if (mDvrPlayer.isPlaybackPrepared()) { @@ -346,8 +358,11 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { private boolean handleIntent(Intent intent, boolean finishActivity) { mProgram = getProgramFromIntent(intent); if (mProgram == null) { - Toast.makeText(getActivity(), getString(R.string.dvr_program_not_found), - Toast.LENGTH_SHORT).show(); + Toast.makeText( + getActivity(), + getString(R.string.dvr_program_not_found), + Toast.LENGTH_SHORT) + .show(); if (finishActivity) { getActivity().finish(); } @@ -359,12 +374,18 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { private void selectBestMatchedTrack(int trackType) { TvTrackInfo selectedTrack = getTrackSetting(trackType); if (selectedTrack != null) { - TvTrackInfo bestMatchedTrack = TvTrackInfoUtils.getBestTrackInfo(getTracks(trackType), - selectedTrack.getId(), selectedTrack.getLanguage(), - trackType == TvTrackInfo.TYPE_AUDIO ? selectedTrack.getAudioChannelCount() : 0); - if (bestMatchedTrack != null && (trackType == TvTrackInfo.TYPE_AUDIO || Utils - .isEqualLanguage(bestMatchedTrack.getLanguage(), - selectedTrack.getLanguage()))) { + TvTrackInfo bestMatchedTrack = + TvTrackInfoUtils.getBestTrackInfo( + getTracks(trackType), + selectedTrack.getId(), + selectedTrack.getLanguage(), + trackType == TvTrackInfo.TYPE_AUDIO + ? selectedTrack.getAudioChannelCount() + : 0); + if (bestMatchedTrack != null + && (trackType == TvTrackInfo.TYPE_AUDIO + || Utils.isEqualLanguage( + bestMatchedTrack.getLanguage(), selectedTrack.getLanguage()))) { selectTrack(trackType, bestMatchedTrack); return; } @@ -421,7 +442,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { } if (mRelatedRecordingsRowAdapter.size() == 0) { mRowsAdapter.remove(mRelatedRecordingsRow); - } else if (wasEmpty){ + } else if (wasEmpty) { mRowsAdapter.add(mRelatedRecordingsRow); } updateVerticalPosition(); @@ -446,8 +467,9 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { private ListRow getRelatedRecordingsRow() { mRelatedRecordingCardPresenter = new DvrPlaybackCardPresenter(getActivity()); mRelatedRecordingsRowAdapter = new RelatedRecordingsAdapter(mRelatedRecordingCardPresenter); - HeaderItem header = new HeaderItem(0, - getActivity().getString(R.string.dvr_playback_related_recordings)); + HeaderItem header = + new HeaderItem( + 0, getActivity().getString(R.string.dvr_playback_related_recordings)); return new ListRow(header, mRelatedRecordingsRowAdapter); } @@ -457,8 +479,8 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { } private long getSeekTimeFromIntent(Intent intent) { - return intent.getLongExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME, - TvInputManager.TIME_SHIFT_INVALID_TIME); + return intent.getLongExtra( + Utils.EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME, TvInputManager.TIME_SHIFT_INVALID_TIME); } private void updateVerticalPosition() { @@ -491,4 +513,4 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment { return item.getId(); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java index e49870f1..b4481df8 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java @@ -26,24 +26,16 @@ import android.transition.Transition; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.android.tv.R; import com.android.tv.util.TvSettings; - import java.util.List; import java.util.Locale; -/** - * Fragment for DVR playback closed-caption/multi-audio settings. - */ +/** Fragment for DVR playback closed-caption/multi-audio settings. */ public class DvrPlaybackSideFragment extends GuidedStepFragment { - /** - * The tag for passing track infos to side fragments. - */ + /** The tag for passing track infos to side fragments. */ public static final String TRACK_INFOS = "dvr_key_track_infos"; - /** - * The tag for passing selected track's ID to side fragments. - */ + /** The tag for passing selected track's ID to side fragments. */ public static final String SELECTED_TRACK_ID = "dvr_key_selected_track_id"; private static final int ACTION_ID_NO_SUBTITLE = -1; @@ -60,39 +52,42 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment { mTrackInfos = getArguments().getParcelableArrayList(TRACK_INFOS); mTrackType = mTrackInfos.get(0).getType(); mSelectedTrackId = getArguments().getString(SELECTED_TRACK_ID); - mOverlayFragment = ((DvrPlaybackOverlayFragment) getFragmentManager() - .findFragmentById(R.id.dvr_playback_controls_fragment)); + mOverlayFragment = + ((DvrPlaybackOverlayFragment) + getFragmentManager().findFragmentById(R.id.dvr_playback_controls_fragment)); super.onCreate(savedInstanceState); } @Override - public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateBackgroundView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View backgroundView = super.onCreateBackgroundView(inflater, container, savedInstanceState); - backgroundView.setBackgroundColor(getResources() - .getColor(R.color.lb_playback_controls_background_light)); + backgroundView.setBackgroundColor( + getResources().getColor(R.color.lb_playback_controls_background_light)); return backgroundView; } @Override public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { if (mTrackType == TvTrackInfo.TYPE_SUBTITLE) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_NO_SUBTITLE) - .title(getString(R.string.closed_caption_option_item_off)) - .checkSetId(CHECK_SET_ID) - .checked(mSelectedTrackId == null) - .build()); + actions.add( + new GuidedAction.Builder(getActivity()) + .id(ACTION_ID_NO_SUBTITLE) + .title(getString(R.string.closed_caption_option_item_off)) + .checkSetId(CHECK_SET_ID) + .checked(mSelectedTrackId == null) + .build()); } for (int i = 0; i < mTrackInfos.size(); i++) { TvTrackInfo info = mTrackInfos.get(i); boolean checked = TextUtils.equals(info.getId(), mSelectedTrackId); - GuidedAction action = new GuidedAction.Builder(getActivity()) - .id(i) - .title(getTrackLabel(info, i)) - .checkSetId(CHECK_SET_ID) - .checked(checked) - .build(); + GuidedAction action = + new GuidedAction.Builder(getActivity()) + .id(i) + .title(getTrackLabel(info, i)) + .checkSetId(CHECK_SET_ID) + .checked(checked) + .build(); actions.add(action); if (checked) { mSelectedTrack = info; @@ -136,8 +131,8 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment { if (track.getLanguage() != null) { return new Locale(track.getLanguage()).getDisplayName(); } - return track.getType() == TvTrackInfo.TYPE_SUBTITLE ? - getString(R.string.closed_caption_unknown_language, trackIndex + 1) + return track.getType() == TvTrackInfo.TYPE_SUBTITLE + ? getString(R.string.closed_caption_unknown_language, trackIndex + 1) : getString(R.string.multi_audio_unknown_language); } @@ -151,4 +146,4 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment { t.excludeTarget(R.id.guidedstep_background, true); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java index 7226c666..85bb31b2 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java +++ b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java @@ -24,9 +24,7 @@ import android.media.tv.TvTrackInfo; import android.media.tv.TvView; import android.text.TextUtils; import android.util.Log; - import com.android.tv.dvr.data.RecordedProgram; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -35,17 +33,13 @@ class DvrPlayer { private static final String TAG = "DvrPlayer"; private static final boolean DEBUG = false; - /** - * The max rewinding speed supported by DVR player. - */ + /** The max rewinding speed supported by DVR player. */ public static final int MAX_REWIND_SPEED = 256; - /** - * The max fast-forwarding speed supported by DVR player. - */ + /** The max fast-forwarding speed supported by DVR player. */ public static final int MAX_FAST_FORWARD_SPEED = 256; private static final long SEEK_POSITION_MARGIN_MS = TimeUnit.SECONDS.toMillis(2); - private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826 + private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826 private RecordedProgram mProgram; private long mInitialSeekPositionMs; @@ -71,49 +65,39 @@ class DvrPlayer { public static class DvrPlayerCallback { /** - * Called when the playback position is changed. The normal updating frequency is - * around 1 sec., which is restricted to the implementation of - * {@link android.media.tv.TvInputService}. + * Called when the playback position is changed. The normal updating frequency is around 1 + * sec., which is restricted to the implementation of {@link + * android.media.tv.TvInputService}. */ - public void onPlaybackPositionChanged(long positionMs) { } - /** - * Called when the playback state or the playback speed is changed. - */ - public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { } - /** - * Called when the playback toward the end. - */ - public void onPlaybackEnded() { } + public void onPlaybackPositionChanged(long positionMs) {} + /** Called when the playback state or the playback speed is changed. */ + public void onPlaybackStateChanged(int playbackState, int playbackSpeed) {} + /** Called when the playback toward the end. */ + public void onPlaybackEnded() {} } public interface OnAspectRatioChangedListener { /** * Called when the Video's aspect ratio is changed. * - * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios. - * Listeners should handle it carefully. + * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios. Listeners + * should handle it carefully. */ void onAspectRatioChanged(float videoAspectRatio); } public interface OnContentBlockedListener { - /** - * Called when the Video's aspect ratio is changed. - */ + /** Called when the Video's aspect ratio is changed. */ void onContentBlocked(TvContentRating rating); } public interface OnTracksAvailabilityChangedListener { - /** - * Called when the Video's subtitle or audio tracks are changed. - */ + /** Called when the Video's subtitle or audio tracks are changed. */ void onTracksAvailabilityChanged(boolean hasClosedCaption, boolean hasMultiAudio); } public interface OnTrackSelectedListener { - /** - * Called when certain subtitle or audio track is selected. - */ + /** Called when certain subtitle or audio track is selected. */ void onTrackSelected(String selectedTrackId); } @@ -143,9 +127,7 @@ class DvrPlayer { mCallback.onPlaybackStateChanged(mPlaybackState, 1); } - /** - * Resumes playback. - */ + /** Resumes playback. */ public void play() throws IllegalStateException { if (DEBUG) Log.d(TAG, "play()"); if (!isPlaybackPrepared()) { @@ -163,9 +145,7 @@ class DvrPlayer { mCallback.onPlaybackStateChanged(mPlaybackState, 1); } - /** - * Pauses playback. - */ + /** Pauses playback. */ public void pause() throws IllegalStateException { if (DEBUG) Log.d(TAG, "pause()"); if (!isPlaybackPrepared()) { @@ -187,8 +167,8 @@ class DvrPlayer { } /** - * Fast-forwards playback with the given speed. If the given speed is larger than - * {@value #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}. + * Fast-forwards playback with the given speed. If the given speed is larger than {@value + * #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}. */ public void fastForward(int speed) throws IllegalStateException { if (DEBUG) Log.d(TAG, "fastForward()"); @@ -209,8 +189,8 @@ class DvrPlayer { } /** - * Rewinds playback with the given speed. If the given speed is larger than - * {@value #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}. + * Rewinds playback with the given speed. If the given speed is larger than {@value + * #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}. */ public void rewind(int speed) throws IllegalStateException { if (DEBUG) Log.d(TAG, "rewind()"); @@ -230,9 +210,7 @@ class DvrPlayer { mCallback.onPlaybackStateChanged(mPlaybackState, speed); } - /** - * Seeks playback to the specified position. - */ + /** Seeks playback to the specified position. */ public void seekTo(long positionMs) throws IllegalStateException { if (DEBUG) Log.d(TAG, "seekTo()"); if (!isPlaybackPrepared()) { @@ -244,17 +222,15 @@ class DvrPlayer { positionMs = getRealSeekPosition(positionMs, SEEK_POSITION_MARGIN_MS); if (DEBUG) Log.d(TAG, "Now: " + getPlaybackPosition() + ", shift to: " + positionMs); mTvView.timeShiftSeekTo(positionMs + mStartPositionMs); - if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING || - mPlaybackState == PlaybackState.STATE_REWINDING) { + if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING + || mPlaybackState == PlaybackState.STATE_REWINDING) { mPlaybackState = PlaybackState.STATE_PLAYING; mTvView.timeShiftResume(); mCallback.onPlaybackStateChanged(mPlaybackState, 1); } } - /** - * Resets playback. - */ + /** Resets playback. */ public void reset() { if (DEBUG) Log.d(TAG, "reset()"); mCallback.onPlaybackStateChanged(PlaybackState.STATE_NONE, 1); @@ -269,9 +245,7 @@ class DvrPlayer { mSelectedSubtitleTrackId = null; } - /** - * Sets callbacks for playback. - */ + /** Sets callbacks for playback. */ public void setCallback(DvrPlayerCallback callback) { if (callback != null) { mCallback = callback; @@ -280,23 +254,17 @@ class DvrPlayer { } } - /** - * Sets the listener to aspect ratio changing. - */ + /** Sets the listener to aspect ratio changing. */ public void setOnAspectRatioChangedListener(OnAspectRatioChangedListener listener) { mOnAspectRatioChangedListener = listener; } - /** - * Sets the listener to content blocking. - */ + /** Sets the listener to content blocking. */ public void setOnContentBlockedListener(OnContentBlockedListener listener) { mOnContentBlockedListener = listener; } - /** - * Sets the listener to tracks changing. - */ + /** Sets the listener to tracks changing. */ public void setOnTracksAvailabilityChangedListener( OnTracksAvailabilityChangedListener listener) { mOnTracksAvailabilityChangedListener = listener; @@ -305,8 +273,8 @@ class DvrPlayer { /** * Sets the listener to tracks of the given type being selected. * - * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO} - * or {@link TvTrackInfo#TYPE_SUBTITLE}. + * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO} or {@link + * TvTrackInfo#TYPE_SUBTITLE}. */ public void setOnTrackSelectedListener(int trackType, OnTrackSelectedListener listener) { if (trackType == TvTrackInfo.TYPE_AUDIO) { @@ -316,9 +284,7 @@ class DvrPlayer { } } - /** - * Gets the listener to tracks of the given type being selected. - */ + /** Gets the listener to tracks of the given type being selected. */ public OnTrackSelectedListener getOnTrackSelectedListener(int trackType) { if (trackType == TvTrackInfo.TYPE_AUDIO) { return mOnAudioTrackSelectedListener; @@ -328,9 +294,7 @@ class DvrPlayer { return null; } - /** - * Sets recorded programs for playback. If the player is playing another program, stops it. - */ + /** Sets recorded programs for playback. If the player is playing another program, stops it. */ public void setProgram(RecordedProgram program, long initialSeekPositionMs) { if (mProgram != null && mProgram.equals(program)) { return; @@ -342,51 +306,37 @@ class DvrPlayer { mProgram = program; } - /** - * Returns the recorded program now playing. - */ + /** Returns the recorded program now playing. */ public RecordedProgram getProgram() { return mProgram; } - /** - * Returns the currrent playback posistion in msecs. - */ + /** Returns the currrent playback posistion in msecs. */ public long getPlaybackPosition() { return mTimeShiftCurrentPositionMs; } - /** - * Returns the playback speed currently used. - */ + /** Returns the playback speed currently used. */ public int getPlaybackSpeed() { return (int) mPlaybackParams.getSpeed(); } - /** - * Returns the playback state defined in {@link android.media.session.PlaybackState}. - */ + /** Returns the playback state defined in {@link android.media.session.PlaybackState}. */ public int getPlaybackState() { return mPlaybackState; } - /** - * Returns the subtitle tracks of the current playback. - */ + /** Returns the subtitle tracks of the current playback. */ public ArrayList<TvTrackInfo> getSubtitleTracks() { return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE)); } - /** - * Returns the audio tracks of the current playback. - */ + /** Returns the audio tracks of the current playback. */ public ArrayList<TvTrackInfo> getAudioTracks() { return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_AUDIO)); } - /** - * Returns the ID of the selected track of the given type. - */ + /** Returns the ID of the selected track of the given type. */ public String getSelectedTrackId(int trackType) { if (trackType == TvTrackInfo.TYPE_AUDIO) { return mSelectedAudioTrackId; @@ -396,9 +346,7 @@ class DvrPlayer { return null; } - /** - * Returns if playback of the recorded program is started. - */ + /** Returns if playback of the recorded program is started. */ public boolean isPlaybackPrepared() { return mPlaybackState != PlaybackState.STATE_NONE && mPlaybackState != PlaybackState.STATE_CONNECTING; @@ -449,125 +397,138 @@ class DvrPlayer { } private void setTvViewCallbacks() { - mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() { - @Override - public void onTimeShiftStartPositionChanged(String inputId, long timeMs) { - if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs); - mStartPositionMs = timeMs; - if (mTimeShiftPlayAvailable) { - resumeToWatchedPositionIfNeeded(); - } - } + mTvView.setTimeShiftPositionCallback( + new TvView.TimeShiftPositionCallback() { + @Override + public void onTimeShiftStartPositionChanged(String inputId, long timeMs) { + if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs); + mStartPositionMs = timeMs; + if (mTimeShiftPlayAvailable) { + resumeToWatchedPositionIfNeeded(); + } + } - @Override - public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) { - if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs); - if (!mTimeShiftPlayAvailable) { - // Workaround of b/31436263 - return; - } - // Workaround of b/32211561, TIF won't report start position when TIS report - // its start position as 0. In that case, we have to do the prework of playback - // on the first time we get current position, and the start position should be 0 - // at that time. - if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) { - mStartPositionMs = 0; - resumeToWatchedPositionIfNeeded(); - } - timeMs -= mStartPositionMs; - if (mPlaybackState == PlaybackState.STATE_REWINDING - && timeMs <= REWIND_POSITION_MARGIN_MS) { - play(); - } else { - mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0); - mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs); - if (timeMs >= mProgram.getDurationMillis()) { - pause(); - mCallback.onPlaybackEnded(); + @Override + public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) { + if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs); + if (!mTimeShiftPlayAvailable) { + // Workaround of b/31436263 + return; + } + // Workaround of b/32211561, TIF won't report start position when TIS report + // its start position as 0. In that case, we have to do the prework of + // playback + // on the first time we get current position, and the start position should + // be 0 + // at that time. + if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) { + mStartPositionMs = 0; + resumeToWatchedPositionIfNeeded(); + } + timeMs -= mStartPositionMs; + if (mPlaybackState == PlaybackState.STATE_REWINDING + && timeMs <= REWIND_POSITION_MARGIN_MS) { + play(); + } else { + mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0); + mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs); + if (timeMs >= mProgram.getDurationMillis()) { + pause(); + mCallback.onPlaybackEnded(); + } + } } - } - } - }); - mTvView.setCallback(new TvView.TvInputCallback() { - @Override - public void onTimeShiftStatusChanged(String inputId, int status) { - if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status); - if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE - && mPlaybackState == PlaybackState.STATE_CONNECTING) { - mTimeShiftPlayAvailable = true; - if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { - // onTimeShiftStatusChanged is sometimes called after - // onTimeShiftStartPositionChanged is called. In this case, - // resumeToWatchedPositionIfNeeded needs to be called here. - resumeToWatchedPositionIfNeeded(); + }); + mTvView.setCallback( + new TvView.TvInputCallback() { + @Override + public void onTimeShiftStatusChanged(String inputId, int status) { + if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status); + if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE + && mPlaybackState == PlaybackState.STATE_CONNECTING) { + mTimeShiftPlayAvailable = true; + if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { + // onTimeShiftStatusChanged is sometimes called after + // onTimeShiftStartPositionChanged is called. In this case, + // resumeToWatchedPositionIfNeeded needs to be called here. + resumeToWatchedPositionIfNeeded(); + } + } } - } - } - - @Override - public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) { - boolean hasClosedCaption = - !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty(); - boolean hasMultiAudio = mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1; - if ((hasClosedCaption != mHasClosedCaption || hasMultiAudio != mHasMultiAudio) - && mOnTracksAvailabilityChangedListener != null) { - mOnTracksAvailabilityChangedListener - .onTracksAvailabilityChanged(hasClosedCaption, hasMultiAudio); - } - mHasClosedCaption = hasClosedCaption; - mHasMultiAudio = hasMultiAudio; - } - @Override - public void onTrackSelected(String inputId, int type, String trackId) { - if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) { - setSelectedTrackId(type, trackId); - OnTrackSelectedListener listener = getOnTrackSelectedListener(type); - if (listener != null) { - listener.onTrackSelected(trackId); + @Override + public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) { + boolean hasClosedCaption = + !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty(); + boolean hasMultiAudio = + mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1; + if ((hasClosedCaption != mHasClosedCaption + || hasMultiAudio != mHasMultiAudio) + && mOnTracksAvailabilityChangedListener != null) { + mOnTracksAvailabilityChangedListener.onTracksAvailabilityChanged( + hasClosedCaption, hasMultiAudio); + } + mHasClosedCaption = hasClosedCaption; + mHasMultiAudio = hasMultiAudio; } - } else if (type == TvTrackInfo.TYPE_VIDEO && trackId != null - && mOnAspectRatioChangedListener != null) { - List<TvTrackInfo> trackInfos = mTvView.getTracks(TvTrackInfo.TYPE_VIDEO); - if (trackInfos != null) { - for (TvTrackInfo trackInfo : trackInfos) { - if (trackInfo.getId().equals(trackId)) { - float videoAspectRatio; - int videoWidth = trackInfo.getVideoWidth(); - int videoHeight = trackInfo.getVideoHeight(); - if (videoWidth > 0 && videoHeight > 0) { - videoAspectRatio = trackInfo.getVideoPixelAspectRatio() - * trackInfo.getVideoWidth() / trackInfo.getVideoHeight(); - } else { - // Aspect ratio is unknown. Pass the message to listeners. - videoAspectRatio = 0; - } - if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio); - if (mAspectRatio != videoAspectRatio || videoAspectRatio == 0) { - mOnAspectRatioChangedListener - .onAspectRatioChanged(videoAspectRatio); - mAspectRatio = videoAspectRatio; - return; + + @Override + public void onTrackSelected(String inputId, int type, String trackId) { + if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) { + setSelectedTrackId(type, trackId); + OnTrackSelectedListener listener = getOnTrackSelectedListener(type); + if (listener != null) { + listener.onTrackSelected(trackId); + } + } else if (type == TvTrackInfo.TYPE_VIDEO + && trackId != null + && mOnAspectRatioChangedListener != null) { + List<TvTrackInfo> trackInfos = + mTvView.getTracks(TvTrackInfo.TYPE_VIDEO); + if (trackInfos != null) { + for (TvTrackInfo trackInfo : trackInfos) { + if (trackInfo.getId().equals(trackId)) { + float videoAspectRatio; + int videoWidth = trackInfo.getVideoWidth(); + int videoHeight = trackInfo.getVideoHeight(); + if (videoWidth > 0 && videoHeight > 0) { + videoAspectRatio = + trackInfo.getVideoPixelAspectRatio() + * trackInfo.getVideoWidth() + / trackInfo.getVideoHeight(); + } else { + // Aspect ratio is unknown. Pass the message to + // listeners. + videoAspectRatio = 0; + } + if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio); + if (mAspectRatio != videoAspectRatio + || videoAspectRatio == 0) { + mOnAspectRatioChangedListener.onAspectRatioChanged( + videoAspectRatio); + mAspectRatio = videoAspectRatio; + return; + } + } } } } } - } - } - @Override - public void onContentBlocked(String inputId, TvContentRating rating) { - if (mOnContentBlockedListener != null) { - mOnContentBlockedListener.onContentBlocked(rating); - } - } - }); + @Override + public void onContentBlocked(String inputId, TvContentRating rating) { + if (mOnContentBlockedListener != null) { + mOnContentBlockedListener.onContentBlocked(rating); + } + } + }); } private void resumeToWatchedPositionIfNeeded() { if (mInitialSeekPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { - mTvView.timeShiftSeekTo(getRealSeekPosition(mInitialSeekPositionMs, - SEEK_POSITION_MARGIN_MS) + mStartPositionMs); + mTvView.timeShiftSeekTo( + getRealSeekPosition(mInitialSeekPositionMs, SEEK_POSITION_MARGIN_MS) + + mStartPositionMs); mInitialSeekPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; } if (mPauseOnPrepared) { @@ -580,4 +541,4 @@ class DvrPlayer { } mCallback.onPlaybackStateChanged(mPlaybackState, 1); } -}
\ No newline at end of file +} |