blob: 7c2d12d9f87c8924607cebbb1621579270d690f0 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.dvr.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.TvSingletons;
import com.android.tv.common.SoftPreconditions;
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 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. */
@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. */
public static final long ID_NOT_SET = 0;
/** 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 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());
/** 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())
.setProgramId(p.getId())
.setProgramTitle(p.getTitle())
.setSeasonNumber(p.getSeasonNumber())
.setEpisodeNumber(p.getEpisodeNumber())
.setEpisodeTitle(p.getEpisodeTitle())
.setProgramDescription(p.getDescription())
.setProgramLongDescription(p.getLongDescription())
.setProgramPosterArtUri(p.getPosterArtUri())
.setProgramThumbnailUri(p.getThumbnailUri())
.setType(TYPE_PROGRAM);
}
public static Builder builder(String inputId, long channelId, long startTime, long endTime) {
return new Builder()
.setInputId(inputId)
.setChannelId(channelId)
.setStartTimeMs(startTime)
.setEndTimeMs(endTime)
.setType(TYPE_TIMED);
}
/** 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()
.setInputId(p.getInputId())
.setChannelId(p.getChannelId())
.setType(isProgramRecording ? TYPE_PROGRAM : TYPE_TIMED)
.setStartTimeMs(p.getStartTimeUtcMillis())
.setEndTimeMs(p.getEndTimeUtcMillis())
.setProgramTitle(p.getTitle())
.setSeasonNumber(p.getSeasonNumber())
.setEpisodeNumber(p.getEpisodeNumber())
.setEpisodeTitle(p.getEpisodeTitle())
.setProgramDescription(p.getDescription())
.setProgramLongDescription(p.getLongDescription())
.setProgramPosterArtUri(p.getPosterArtUri())
.setProgramThumbnailUri(p.getThumbnailUri())
.setState(STATE_RECORDING_FINISHED)
.setRecordedProgramId(p.getId());
}
public static final class Builder {
private long mId = ID_NOT_SET;
private long mPriority = DvrScheduleManager.DEFAULT_PRIORITY;
private String mInputId;
private long mChannelId;
private long mProgramId = ID_NOT_SET;
private String mProgramTitle;
private @RecordingType int mType;
private long mStartTimeMs;
private long mEndTimeMs;
private String mSeasonNumber;
private String mEpisodeNumber;
private String mEpisodeTitle;
private String mProgramDescription;
private String mProgramLongDescription;
private String mProgramPosterArtUri;
private String mProgramThumbnailUri;
private @RecordingState int mState;
private long mSeriesRecordingId = ID_NOT_SET;
private Long mRecodedProgramId;
private Integer mFailedReason;
private Builder() {}
public Builder setId(long id) {
mId = id;
return this;
}
public Builder setPriority(long priority) {
mPriority = priority;
return this;
}
public Builder setInputId(String inputId) {
mInputId = inputId;
return this;
}
public Builder setChannelId(long channelId) {
mChannelId = channelId;
return this;
}
public Builder setProgramId(long programId) {
mProgramId = programId;
return this;
}
public Builder setProgramTitle(String programTitle) {
mProgramTitle = programTitle;
return this;
}
private Builder setType(@RecordingType int type) {
mType = type;
return this;
}
public Builder setStartTimeMs(long startTimeMs) {
mStartTimeMs = startTimeMs;
return this;
}
public Builder setEndTimeMs(long endTimeMs) {
mEndTimeMs = endTimeMs;
return this;
}
public Builder setSeasonNumber(String seasonNumber) {
mSeasonNumber = seasonNumber;
return this;
}
public Builder setEpisodeNumber(String episodeNumber) {
mEpisodeNumber = episodeNumber;
return this;
}
public Builder setEpisodeTitle(String episodeTitle) {
mEpisodeTitle = episodeTitle;
return this;
}
public Builder setProgramDescription(String description) {
mProgramDescription = description;
return this;
}
public Builder setProgramLongDescription(String longDescription) {
mProgramLongDescription = longDescription;
return this;
}
public Builder setProgramPosterArtUri(String programPosterArtUri) {
mProgramPosterArtUri = programPosterArtUri;
return this;
}
public Builder setProgramThumbnailUri(String programThumbnailUri) {
mProgramThumbnailUri = programThumbnailUri;
return this;
}
public Builder setState(@RecordingState int state) {
mState = state;
return this;
}
public Builder setSeriesRecordingId(long seriesRecordingId) {
mSeriesRecordingId = seriesRecordingId;
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,
mRecodedProgramId,
mFailedReason);
}
}
/** Creates {@link Builder} object from the given original {@code Recording}. */
public static Builder buildFrom(ScheduledRecording orig) {
return new Builder()
.setId(orig.mId)
.setInputId(orig.mInputId)
.setChannelId(orig.mChannelId)
.setEndTimeMs(orig.mEndTimeMs)
.setSeriesRecordingId(orig.mSeriesRecordingId)
.setPriority(orig.mPriority)
.setProgramId(orig.mProgramId)
.setProgramTitle(orig.mProgramTitle)
.setStartTimeMs(orig.mStartTimeMs)
.setSeasonNumber(orig.getSeasonNumber())
.setEpisodeNumber(orig.getEpisodeNumber())
.setEpisodeTitle(orig.getEpisodeTitle())
.setProgramDescription(orig.getProgramDescription())
.setProgramLongDescription(orig.getProgramLongDescription())
.setProgramPosterArtUri(orig.getProgramPosterArtUri())
.setProgramThumbnailUri(orig.getProgramThumbnailUri())
.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
})
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;
public static final int STATE_RECORDING_FAILED = 3;
public static final int STATE_RECORDING_CLIPPED = 4;
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. */
public static final int TYPE_TIMED = 1;
/** 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}.
*/
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_FAILED_REASON,
Schedules.COLUMN_SERIES_RECORDING_ID
};
/** Creates {@link ScheduledRecording} object from the given {@link Cursor}. */
public static ScheduledRecording fromCursor(Cursor c) {
int index = -1;
return new Builder()
.setId(c.getLong(++index))
.setPriority(c.getLong(++index))
.setType(recordingType(c.getString(++index)))
.setInputId(c.getString(++index))
.setChannelId(c.getLong(++index))
.setProgramId(c.getLong(++index))
.setProgramTitle(c.getString(++index))
.setStartTimeMs(c.getLong(++index))
.setEndTimeMs(c.getLong(++index))
.setSeasonNumber(c.getString(++index))
.setEpisodeNumber(c.getString(++index))
.setEpisodeTitle(c.getString(++index))
.setProgramDescription(c.getString(++index))
.setProgramLongDescription(c.getString(++index))
.setProgramPosterArtUri(c.getString(++index))
.setProgramThumbnailUri(c.getString(++index))
.setState(recordingState(c.getString(++index)))
.setFailedReason(recordingFailedReason(c.getString(++index)))
.setSeriesRecordingId(c.getLong(++index))
.build();
}
public static ContentValues toContentValues(ScheduledRecording r) {
ContentValues values = new ContentValues();
if (r.getId() != ID_NOT_SET) {
values.put(Schedules._ID, r.getId());
}
values.put(Schedules.COLUMN_INPUT_ID, r.getInputId());
values.put(Schedules.COLUMN_CHANNEL_ID, r.getChannelId());
values.put(Schedules.COLUMN_PROGRAM_ID, r.getProgramId());
values.put(Schedules.COLUMN_PROGRAM_TITLE, r.getProgramTitle());
values.put(Schedules.COLUMN_PRIORITY, r.getPriority());
values.put(Schedules.COLUMN_START_TIME_UTC_MILLIS, r.getStartTimeMs());
values.put(Schedules.COLUMN_END_TIME_UTC_MILLIS, r.getEndTimeMs());
values.put(Schedules.COLUMN_SEASON_NUMBER, r.getSeasonNumber());
values.put(Schedules.COLUMN_EPISODE_NUMBER, r.getEpisodeNumber());
values.put(Schedules.COLUMN_EPISODE_TITLE, r.getEpisodeTitle());
values.put(Schedules.COLUMN_PROGRAM_DESCRIPTION, r.getProgramDescription());
values.put(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, r.getProgramLongDescription());
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());
} else {
values.putNull(Schedules.COLUMN_SERIES_RECORDING_ID);
}
return values;
}
public static ScheduledRecording fromParcel(Parcel in) {
return new Builder()
.setId(in.readLong())
.setPriority(in.readLong())
.setInputId(in.readString())
.setChannelId(in.readLong())
.setProgramId(in.readLong())
.setProgramTitle(in.readString())
.setType(in.readInt())
.setStartTimeMs(in.readLong())
.setEndTimeMs(in.readLong())
.setSeasonNumber(in.readString())
.setEpisodeNumber(in.readString())
.setEpisodeTitle(in.readString())
.setProgramDescription(in.readString())
.setProgramLongDescription(in.readString())
.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 */
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
* wins.
*/
private final long mPriority;
private final String mInputId;
private final long mChannelId;
/** Optional id of the associated program. */
private final long mProgramId;
private final String mProgramTitle;
private final long mStartTimeMs;
private final long mEndTimeMs;
private final String mSeasonNumber;
private final String mEpisodeNumber;
private final String mEpisodeTitle;
private final String mProgramDescription;
private final String mProgramLongDescription;
private final String mProgramPosterArtUri;
private final String mProgramThumbnailUri;
@RecordingState private final int mState;
private final long mSeriesRecordingId;
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;
mChannelId = channelId;
mProgramId = programId;
mProgramTitle = programTitle;
mType = type;
mStartTimeMs = startTime;
mEndTimeMs = endTime;
mSeasonNumber = seasonNumber;
mEpisodeNumber = episodeNumber;
mEpisodeTitle = episodeTitle;
mProgramDescription = programDescription;
mProgramLongDescription = programLongDescription;
mProgramPosterArtUri = programPosterArtUri;
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}.
*/
@RecordingType
public int getType() {
return mType;
}
/** Returns schedules' input id. */
public String getInputId() {
return mInputId;
}
/** Returns recorded {@link Channel}. */
public long getChannelId() {
return mChannelId;
}
/** Return the optional program id */
public long getProgramId() {
return mProgramId;
}
/** Return the optional program Title */
public String getProgramTitle() {
return mProgramTitle;
}
/** Returns started time. */
public long getStartTimeMs() {
return mStartTimeMs;
}
/** Returns ended time. */
public long getEndTimeMs() {
return mEndTimeMs;
}
/** Returns the season number. */
public String getSeasonNumber() {
return mSeasonNumber;
}
/** Returns the episode number. */
public String getEpisodeNumber() {
return mEpisodeNumber;
}
/** Returns the episode title. */
public String getEpisodeTitle() {
return mEpisodeTitle;
}
/** Returns the description of program. */
public String getProgramDescription() {
return mProgramDescription;
}
/** Returns the long description of program. */
public String getProgramLongDescription() {
return mProgramLongDescription;
}
/** Returns the poster uri of program. */
public String getProgramPosterArtUri() {
return mProgramPosterArtUri;
}
/** Returns the thumb nail uri of program. */
public String getProgramThumbnailUri() {
return mProgramThumbnailUri;
}
/** 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}.
*/
@RecordingState
public int getState() {
return mState;
}
/** 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; */
public void setId(long id) {
mId = id;
}
public long getPriority() {
return mPriority;
}
/** 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);
} else {
return String.format(
context.getResources().getString(R.string.display_episode_title_format),
mSeasonNumber,
mEpisodeNumber,
episodeTitle);
}
}
return mEpisodeTitle;
}
/**
* Returns the program's display title, if the program title is not null, returns program title.
* Otherwise returns the channel name.
*/
public String getProgramDisplayTitle(Context context) {
if (!TextUtils.isEmpty(mProgramTitle)) {
return mProgramTitle;
}
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}. */
private static @RecordingType int recordingType(String type) {
switch (type) {
case Schedules.TYPE_TIMED:
return TYPE_TIMED;
case Schedules.TYPE_PROGRAM:
return TYPE_PROGRAM;
default:
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}. */
private static String recordingType(@RecordingType int type) {
switch (type) {
case TYPE_TIMED:
return Schedules.TYPE_TIMED;
case TYPE_PROGRAM:
return Schedules.TYPE_PROGRAM;
default:
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}.
*/
private static @RecordingState int recordingState(String state) {
switch (state) {
case Schedules.STATE_RECORDING_NOT_STARTED:
return STATE_RECORDING_NOT_STARTED;
case Schedules.STATE_RECORDING_IN_PROGRESS:
return STATE_RECORDING_IN_PROGRESS;
case Schedules.STATE_RECORDING_FINISHED:
return STATE_RECORDING_FINISHED;
case Schedules.STATE_RECORDING_FAILED:
return STATE_RECORDING_FAILED;
case Schedules.STATE_RECORDING_CLIPPED:
return STATE_RECORDING_CLIPPED;
case Schedules.STATE_RECORDING_DELETED:
return STATE_RECORDING_DELETED;
case Schedules.STATE_RECORDING_CANCELED:
return STATE_RECORDING_CANCELED;
default:
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}.
*/
private static String recordingState(@RecordingState int state) {
switch (state) {
case STATE_RECORDING_NOT_STARTED:
return Schedules.STATE_RECORDING_NOT_STARTED;
case STATE_RECORDING_IN_PROGRESS:
return Schedules.STATE_RECORDING_IN_PROGRESS;
case STATE_RECORDING_FINISHED:
return Schedules.STATE_RECORDING_FINISHED;
case STATE_RECORDING_FAILED:
return Schedules.STATE_RECORDING_FAILED;
case STATE_RECORDING_CLIPPED:
return Schedules.STATE_RECORDING_CLIPPED;
case STATE_RECORDING_DELETED:
return Schedules.STATE_RECORDING_DELETED;
case STATE_RECORDING_CANCELED:
return Schedules.STATE_RECORDING_CANCELED;
default:
SoftPreconditions.checkArgument(false, TAG, "Unknown recording state %s", state);
return Schedules.STATE_RECORDING_NOT_STARTED;
}
}
/**
* Converts a string to a failed reason integer, defaulting to {@link
* #FAILED_REASON_OTHER}.
*/
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;
}
}
/**
* 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
+ "]"
+ "(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
+ ")";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int paramInt) {
out.writeLong(mId);
out.writeLong(mPriority);
out.writeString(mInputId);
out.writeLong(mChannelId);
out.writeLong(mProgramId);
out.writeString(mProgramTitle);
out.writeInt(mType);
out.writeLong(mStartTimeMs);
out.writeLong(mEndTimeMs);
out.writeString(mSeasonNumber);
out.writeString(mEpisodeNumber);
out.writeString(mEpisodeTitle);
out.writeString(mProgramDescription);
out.writeString(mProgramLongDescription);
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}. */
public boolean isNotStarted() {
return mState == STATE_RECORDING_NOT_STARTED;
}
/** 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)) {
return false;
}
ScheduledRecording r = (ScheduledRecording) obj;
return mId == r.mId
&& mPriority == r.mPriority
&& mChannelId == r.mChannelId
&& mProgramId == r.mProgramId
&& Objects.equals(mProgramTitle, r.mProgramTitle)
&& mType == r.mType
&& mStartTimeMs == r.mStartTimeMs
&& mEndTimeMs == r.mEndTimeMs
&& Objects.equals(mSeasonNumber, r.mSeasonNumber)
&& Objects.equals(mEpisodeNumber, r.mEpisodeNumber)
&& Objects.equals(mEpisodeTitle, r.mEpisodeTitle)
&& Objects.equals(mProgramDescription, r.getProgramDescription())
&& Objects.equals(mProgramLongDescription, r.getProgramLongDescription())
&& 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,
mFailedReason,
mSeriesRecordingId);
}
/** 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()]);
}
}