1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.testing; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.media.tv.TvContentRating; 22 import android.media.tv.TvContract; 23 24 import java.util.Objects; 25 import java.util.concurrent.TimeUnit; 26 27 public final class ProgramInfo { 28 /** 29 * If this is specify for title, it will be generated by adding index. 30 */ 31 public static final String GEN_TITLE = ""; 32 33 /** 34 * If this is specify for episode title, it will be generated by adding index. 35 * Also, season and episode numbers would be generated, too. 36 * see: {@link #build} for detail. 37 */ 38 public static final String GEN_EPISODE = ""; 39 private static final int SEASON_MAX = 10; 40 private static final int EPISODE_MAX = 12; 41 42 /** 43 * If this is specify for poster art, 44 * it will be selected one of {@link #POSTER_ARTS_RES} in order. 45 */ 46 public static final String GEN_POSTER = "GEN"; 47 private static final int[] POSTER_ARTS_RES = { 48 0, 49 R.drawable.blue, 50 R.drawable.red_large, 51 R.drawable.green, 52 R.drawable.red, 53 R.drawable.green_large, 54 R.drawable.blue_small}; 55 56 /** 57 * If this is specified for duration, 58 * it will be selected one of {@link #DURATIONS_MS} in order. 59 */ 60 public static final int GEN_DURATION = -1; 61 private static final long[] DURATIONS_MS = { 62 TimeUnit.MINUTES.toMillis(15), 63 TimeUnit.MINUTES.toMillis(45), 64 TimeUnit.MINUTES.toMillis(90), 65 TimeUnit.MINUTES.toMillis(60), 66 TimeUnit.MINUTES.toMillis(30), 67 TimeUnit.MINUTES.toMillis(45), 68 TimeUnit.MINUTES.toMillis(60), 69 TimeUnit.MINUTES.toMillis(90), 70 TimeUnit.HOURS.toMillis(5)}; 71 private static long DURATIONS_SUM_MS; 72 static { 73 DURATIONS_SUM_MS = 0; 74 for (long duration : DURATIONS_MS) { 75 DURATIONS_SUM_MS += duration; 76 } 77 } 78 79 /** 80 * If this is specified for genre, 81 * it will be selected one of {@link #GENRES} in order. 82 */ 83 public static final String GEN_GENRE = "GEN"; 84 private static final String[] GENRES = { 85 "", 86 TvContract.Programs.Genres.SPORTS, 87 TvContract.Programs.Genres.NEWS, 88 TvContract.Programs.Genres.SHOPPING, 89 TvContract.Programs.Genres.DRAMA, 90 TvContract.Programs.Genres.ENTERTAINMENT}; 91 92 public final String title; 93 public final String episode; 94 public final int seasonNumber; 95 public final int episodeNumber; 96 public final String posterArtUri; 97 public final String description; 98 public final long durationMs; 99 public final String genre; 100 public final TvContentRating[] contentRatings; 101 public final String resourceUri; 102 fromCursor(Cursor c)103 public static ProgramInfo fromCursor(Cursor c) { 104 // TODO: Fill other fields. 105 Builder builder = new Builder(); 106 int index = c.getColumnIndex(TvContract.Programs.COLUMN_TITLE); 107 if (index >= 0) { 108 builder.setTitle(c.getString(index)); 109 } 110 index = c.getColumnIndex(TvContract.Programs.COLUMN_SHORT_DESCRIPTION); 111 if (index >= 0) { 112 builder.setDescription(c.getString(index)); 113 } 114 index = c.getColumnIndex(TvContract.Programs.COLUMN_EPISODE_TITLE); 115 if (index >= 0) { 116 builder.setEpisode(c.getString(index)); 117 } 118 return builder.build(); 119 } 120 ProgramInfo(String title, String episode, int seasonNumber, int episodeNumber, String posterArtUri, String description, long durationMs, TvContentRating[] contentRatings, String genre, String resourceUri)121 public ProgramInfo(String title, String episode, int seasonNumber, int episodeNumber, 122 String posterArtUri, String description, long durationMs, 123 TvContentRating[] contentRatings, String genre, String resourceUri) { 124 this.title = title; 125 this.episode = episode; 126 this.seasonNumber = seasonNumber; 127 this.episodeNumber = episodeNumber; 128 this.posterArtUri = posterArtUri; 129 this.description = description; 130 this.durationMs = durationMs; 131 this.contentRatings = contentRatings; 132 this.genre = genre; 133 this.resourceUri = resourceUri; 134 } 135 136 /** 137 * Create a instance of {@link ProgramInfo} whose content will be generated as much as possible. 138 */ create()139 public static ProgramInfo create() { 140 return new Builder().build(); 141 } 142 143 /** 144 * Get index of the program whose start time equals or less than {@code timeMs} and 145 * end time more than {@code timeMs}. 146 * @param timeMs target time in millis to find a program. 147 * @param channelId used to add complexity to the index between two consequence channels. 148 */ getIndex(long timeMs, long channelId)149 public int getIndex(long timeMs, long channelId) { 150 if (durationMs != GEN_DURATION) { 151 return Math.max((int) (timeMs / durationMs), 0); 152 } 153 long startTimeMs = channelId * DURATIONS_MS[((int) (channelId % DURATIONS_MS.length))]; 154 int index = (int) ((timeMs - startTimeMs) / DURATIONS_SUM_MS) * DURATIONS_MS.length; 155 startTimeMs += (index / DURATIONS_MS.length) * DURATIONS_SUM_MS; 156 while (startTimeMs + DURATIONS_MS[index % DURATIONS_MS.length] < timeMs) { 157 startTimeMs += DURATIONS_MS[index % DURATIONS_MS.length]; 158 index++; 159 } 160 return index; 161 } 162 163 /** 164 * Returns the start time for the program with the position. 165 * @param index index returned by {@link #getIndex} 166 */ getStartTimeMs(int index, long channelId)167 public long getStartTimeMs(int index, long channelId) { 168 if (durationMs != GEN_DURATION) { 169 return index * durationMs; 170 } 171 long startTimeMs = channelId * DURATIONS_MS[((int) (channelId % DURATIONS_MS.length))] 172 + (index / DURATIONS_MS.length) * DURATIONS_SUM_MS; 173 for (int i = 0; i < index % DURATIONS_MS.length; i++) { 174 startTimeMs += DURATIONS_MS[i]; 175 } 176 return startTimeMs; 177 } 178 179 /** 180 * Return complete {@link ProgramInfo} with the generated value. 181 * See: {@link #GEN_TITLE}, {@link #GEN_EPISODE}, {@link #GEN_POSTER}, {@link #GEN_DURATION}, 182 * {@link #GEN_GENRE}. 183 * @param index index returned by {@link #getIndex} 184 */ build(Context context, int index)185 public ProgramInfo build(Context context, int index) { 186 if (!GEN_TITLE.equals(title) 187 && episode == null 188 && !GEN_POSTER.equals(posterArtUri) 189 && durationMs != GEN_DURATION 190 && !GEN_GENRE.equals(genre)) { 191 return this; 192 } 193 return new ProgramInfo( 194 GEN_TITLE.equals(title) ? "Title(" + index + ")" : title, 195 GEN_EPISODE.equals(episode) ? "Episode(" + index + ")" : episode, 196 episode != null ? (index % SEASON_MAX + 1) : seasonNumber, 197 episode != null ? (index % EPISODE_MAX + 1) : episodeNumber, 198 GEN_POSTER.equals(posterArtUri) 199 ? Utils.getUriStringForResource(context, 200 POSTER_ARTS_RES[index % POSTER_ARTS_RES.length]) 201 : posterArtUri, 202 description, 203 durationMs == GEN_DURATION ? DURATIONS_MS[index % DURATIONS_MS.length] : durationMs, 204 contentRatings, 205 GEN_GENRE.equals(genre) ? GENRES[index % GENRES.length] : genre, 206 resourceUri); 207 } 208 209 @Override toString()210 public String toString() { 211 return "ProgramInfo{title=" + title 212 + ", episode=" + episode 213 + ", durationMs=" + durationMs + "}"; 214 } 215 216 @Override equals(Object o)217 public boolean equals(Object o) { 218 if (this == o) { 219 return true; 220 } 221 if (o == null || getClass() != o.getClass()) { 222 return false; 223 } 224 ProgramInfo that = (ProgramInfo) o; 225 return Objects.equals(seasonNumber, that.seasonNumber) && 226 Objects.equals(episodeNumber, that.episodeNumber) && 227 Objects.equals(durationMs, that.durationMs) && 228 Objects.equals(title, that.title) && 229 Objects.equals(episode, that.episode) && 230 Objects.equals(posterArtUri, that.posterArtUri) && 231 Objects.equals(description, that.description) && 232 Objects.equals(genre, that.genre) && 233 Objects.equals(contentRatings, that.contentRatings) && 234 Objects.equals(resourceUri, that.resourceUri); 235 } 236 237 @Override hashCode()238 public int hashCode() { 239 return Objects.hash(title, episode, seasonNumber, episodeNumber); 240 } 241 242 public static class Builder { 243 private String mTitle = GEN_TITLE; 244 private String mEpisode = GEN_EPISODE; 245 private int mSeasonNumber; 246 private int mEpisodeNumber; 247 private String mPosterArtUri = GEN_POSTER; 248 private String mDescription; 249 private long mDurationMs = GEN_DURATION; 250 private TvContentRating[] mContentRatings; 251 private String mGenre = GEN_GENRE; 252 private String mResourceUri; 253 setTitle(String title)254 public Builder setTitle(String title) { 255 mTitle = title; 256 return this; 257 } 258 setEpisode(String episode)259 public Builder setEpisode(String episode) { 260 mEpisode = episode; 261 return this; 262 } 263 setSeasonNumber(int seasonNumber)264 public Builder setSeasonNumber(int seasonNumber) { 265 mSeasonNumber = seasonNumber; 266 return this; 267 } 268 setEpisodeNumber(int episodeNumber)269 public Builder setEpisodeNumber(int episodeNumber) { 270 mEpisodeNumber = episodeNumber; 271 return this; 272 } 273 setPosterArtUri(String posterArtUri)274 public Builder setPosterArtUri(String posterArtUri) { 275 mPosterArtUri = posterArtUri; 276 return this; 277 } 278 setDescription(String description)279 public Builder setDescription(String description) { 280 mDescription = description; 281 return this; 282 } 283 setDurationMs(long durationMs)284 public Builder setDurationMs(long durationMs) { 285 mDurationMs = durationMs; 286 return this; 287 } 288 setContentRatings(TvContentRating[] contentRatings)289 public Builder setContentRatings(TvContentRating[] contentRatings) { 290 mContentRatings = contentRatings; 291 return this; 292 } 293 setGenre(String genre)294 public Builder setGenre(String genre) { 295 mGenre = genre; 296 return this; 297 } 298 setResourceUri(String resourceUri)299 public Builder setResourceUri(String resourceUri) { 300 mResourceUri = resourceUri; 301 return this; 302 } 303 build()304 public ProgramInfo build() { 305 return new ProgramInfo(mTitle, mEpisode, mSeasonNumber, mEpisodeNumber, mPosterArtUri, 306 mDescription, mDurationMs, mContentRatings, mGenre, mResourceUri); 307 } 308 } 309 } 310