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