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