1/*
2 * Copyright (C) 2014 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
17package android.media.tv;
18
19import android.annotation.NonNull;
20import android.annotation.SystemApi;
21import android.text.TextUtils;
22
23import com.android.internal.util.Preconditions;
24
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.List;
28import java.util.Objects;
29
30/**
31 * A class representing a TV content rating. When a TV input service inserts the content rating
32 * information on a program into the database, this class can be used to generate the formatted
33 * string for
34 * {@link TvContract.Programs#COLUMN_CONTENT_RATING TvContract.Programs.COLUMN_CONTENT_RATING}.
35 * To create a {@code TvContentRating} object, use the
36 * {@link #createRating TvContentRating.createRating} method with valid rating system string
37 * constants.
38 *
39 * <p>It is possible for an application to define its own content rating system by supplying a
40 * content rating system definition XML resource (see example below) and declaring a broadcast
41 * receiver that filters {@link TvInputManager#ACTION_QUERY_CONTENT_RATING_SYSTEMS} in its manifest.
42 *
43 * <h3> Example: Rating system definition for the TV Parental Guidelines</h3>
44 * The following XML example shows how the TV Parental Guidelines in the United States can be
45 * defined:
46 * <p><pre class="prettyprint">
47 * {@literal
48 * <rating-system-definitions xmlns:android="http://schemas.android.com/apk/res/android"
49 *     android:versionCode="1">
50 *     <rating-system-definition android:name="US_TV"
51 *         android:country="US"
52 *         android:description="@string/description_us_tv">
53 *         <sub-rating-definition android:name="US_TV_D"
54 *             android:title="D"
55 *             android:description="@string/description_us_tv_d" />
56 *         <sub-rating-definition android:name="US_TV_L"
57 *             android:title="L"
58 *             android:description="@string/description_us_tv_l" />
59 *         <sub-rating-definition android:name="US_TV_S"
60 *             android:title="S"
61 *             android:description="@string/description_us_tv_s" />
62 *         <sub-rating-definition android:name="US_TV_V"
63 *             android:title="V"
64 *             android:description="@string/description_us_tv_v" />
65 *         <sub-rating-definition android:name="US_TV_FV"
66 *             android:title="FV"
67 *             android:description="@string/description_us_tv_fv" />
68 *
69 *         <rating-definition android:name="US_TV_Y"
70 *             android:title="TV-Y"
71 *             android:description="@string/description_us_tv_y"
72 *             android:icon="@drawable/icon_us_tv_y"
73 *             android:contentAgeHint="0" />
74 *         <rating-definition android:name="US_TV_Y7"
75 *             android:title="TV-Y7"
76 *             android:description="@string/description_us_tv_y7"
77 *             android:icon="@drawable/icon_us_tv_y7"
78 *             android:contentAgeHint="7">
79 *             <sub-rating android:name="US_TV_FV" />
80 *         </rating-definition>
81 *         <rating-definition android:name="US_TV_G"
82 *             android:title="TV-G"
83 *             android:description="@string/description_us_tv_g"
84 *             android:icon="@drawable/icon_us_tv_g"
85 *             android:contentAgeHint="0" />
86 *         <rating-definition android:name="US_TV_PG"
87 *             android:title="TV-PG"
88 *             android:description="@string/description_us_tv_pg"
89 *             android:icon="@drawable/icon_us_tv_pg"
90 *             android:contentAgeHint="14">
91 *             <sub-rating android:name="US_TV_D" />
92 *             <sub-rating android:name="US_TV_L" />
93 *             <sub-rating android:name="US_TV_S" />
94 *             <sub-rating android:name="US_TV_V" />
95 *         </rating-definition>
96 *         <rating-definition android:name="US_TV_14"
97 *             android:title="TV-14"
98 *             android:description="@string/description_us_tv_14"
99 *             android:icon="@drawable/icon_us_tv_14"
100 *             android:contentAgeHint="14">
101 *             <sub-rating android:name="US_TV_D" />
102 *             <sub-rating android:name="US_TV_L" />
103 *             <sub-rating android:name="US_TV_S" />
104 *             <sub-rating android:name="US_TV_V" />
105 *         </rating-definition>
106 *         <rating-definition android:name="US_TV_MA"
107 *             android:title="TV-MA"
108 *             android:description="@string/description_us_tv_ma"
109 *             android:icon="@drawable/icon_us_tv_ma"
110 *             android:contentAgeHint="17">
111 *             <sub-rating android:name="US_TV_L" />
112 *             <sub-rating android:name="US_TV_S" />
113 *             <sub-rating android:name="US_TV_V" />
114 *         </rating-definition>
115 *         <rating-order>
116 *             <rating android:name="US_TV_Y" />
117 *             <rating android:name="US_TV_Y7" />
118 *         </rating-order>
119 *         <rating-order>
120 *             <rating android:name="US_TV_G" />
121 *             <rating android:name="US_TV_PG" />
122 *             <rating android:name="US_TV_14" />
123 *             <rating android:name="US_TV_MA" />
124 *         </rating-order>
125 *     </rating-system-definition>
126 * </rating-system-definitions>}</pre>
127 *
128 * <h3>System defined rating strings</h3>
129 * The following strings are defined by the system to provide a standard way to create
130 * {@code TvContentRating} objects.
131 *
132 * <p>For example, to create an object that represents TV-PG rating with suggestive dialogue and
133 * coarse language from the TV Parental Guidelines in the United States, one can use the following
134 * code snippet:
135 *
136 * <pre>
137 * TvContentRating rating = TvContentRating.createRating(
138 *         "com.android.tv",
139 *         "US_TV",
140 *         "US_TV_PG",
141 *         "US_TV_D", "US_TV_L");
142 * </pre>
143 * <h4>System defined string for domains</h4>
144 * <table>
145 *     <tr>
146 *         <th>Constant Value</th>
147 *         <th>Description</th>
148 *     </tr>
149 *     <tr>
150 *         <td>com.android.tv</td>
151 *         <td>Used for creating system defined content ratings</td>
152 *     </tr>
153 * </table>
154 *
155 * <h4>System defined strings for rating systems</h4>
156 * <table>
157 *     <tr>
158 *         <th>Constant Value</th>
159 *         <th>Description</th>
160 *     </tr>
161 *     <tr>
162 *         <td>AR_TV</td>
163 *         <td>TV content rating system for Argentina</td>
164 *     </tr>
165 *     <tr>
166 *         <td>AU_TV</td>
167 *         <td>TV content rating system for Australia</td>
168 *     </tr>
169 *     <tr>
170 *         <td>BR_TV</td>
171 *         <td>TV content rating system for Brazil</td>
172 *     </tr>
173 *     <tr>
174 *         <td>CA_TV_EN</td>
175 *         <td>TV content rating system for Canada (English)</td>
176 *     </tr>
177 *     <tr>
178 *         <td>CA_TV_FR</td>
179 *         <td>TV content rating system for Canada (French)</td>
180 *     </tr>
181 *     <tr>
182 *         <td>DVB</td>
183 *         <td>DVB content rating system</td>
184 *     </tr>
185 *     <tr>
186 *         <td>ES_DVB</td>
187 *         <td>DVB content rating system for Spain</td>
188 *     </tr>
189 *     <tr>
190 *         <td>FR_DVB</td>
191 *         <td>DVB content rating system for France</td>
192 *     </tr>
193 *     <tr>
194 *         <td>ISDB</td>
195 *         <td>ISDB content rating system</td>
196 *     </tr>
197 *     <tr>
198 *         <td>KR_TV</td>
199 *         <td>TV content rating system for South Korea</td>
200 *     </tr>
201 *     <tr>
202 *         <td>SG_TV</td>
203 *         <td>TV content rating system for Singapore</td>
204 *     </tr>
205 *     <tr>
206 *         <td>US_MV</td>
207 *         <td>Movie content rating system for the United States</td>
208 *     </tr>
209 *     <tr>
210 *         <td>US_TV</td>
211 *         <td>TV content rating system for the United States</td>
212 *     </tr>
213 * </table>
214 *
215 * <h4>System defined strings for ratings</h4>
216 * <table>
217 *     <tr>
218 *         <th>Rating System</th>
219 *         <th>Constant Value</th>
220 *         <th>Description</th>
221 *     </tr>
222 *     <tr>
223 *         <td valign="top" rowspan="4">AR_TV</td>
224 *         <td>AR_TV_ATP</td>
225 *         <td>Suitable for all audiences. Programs may contain mild violence, language and mature
226 *         situations</td>
227 *     </tr>
228 *     <tr>
229 *         <td>AR_TV_SAM_13</td>
230 *         <td>Suitable for ages 13 and up. Programs may contain mild to moderate language and mild
231 *         violence and sexual references</td>
232 *     </tr>
233 *     <tr>
234 *         <td>AR_TV_SAM_16</td>
235 *         <td>Suitable for ages 16 and up. Programs may contain more intensive violence and coarse
236 *         language, partial nudity and moderate sexual references</td>
237 *     </tr>
238 *     <tr>
239 *         <td>AR_TV_SAM_18</td>
240 *         <td>Suitable for mature audiences only. Programs contain strong violence, coarse language
241 *         and explicit sexual references</td>
242 *     </tr>
243 *     <tr>
244 *         <td valign="top" rowspan="8">AU_TV</td>
245 *         <td>AU_TV_P</td>
246 *         <td>Recommended for younger children aged between 2 and 11 years</td>
247 *     </tr>
248 *     <tr>
249 *         <td>AU_TV_C</td>
250 *         <td>Recommended for older children aged between 5 and 14 years</td>
251 *     </tr>
252 *     <tr>
253 *         <td>AU_TV_G</td>
254 *         <td>Recommended for all ages</td>
255 *     </tr>
256 *     <tr>
257 *         <td>AU_TV_PG</td>
258 *         <td>Parental guidance is recommended for young viewers under 15</td>
259 *     </tr>
260 *     <tr>
261 *         <td>AU_TV_M</td>
262 *         <td>Recommended for mature audiences aged 15 years and over</td>
263 *     </tr>
264 *     <tr>
265 *         <td>AU_TV_MA</td>
266 *         <td>Not suitable for children and teens under 15, due to sexual descriptions, course
267 *         language, adult themes or drug use</td>
268 *     </tr>
269 *     <tr>
270 *         <td>AU_TV_AV</td>
271 *         <td>Not suitable for children and teens under 15. This category is used specifically for
272 *         violent programs</td>
273 *     </tr>
274 *     <tr>
275 *         <td>AU_TV_R</td>
276 *         <td>Not for children under 18. Content may include graphic violence, sexual situations,
277 *         coarse language and explicit drug use</td>
278 *     </tr>
279 *     <tr>
280 *         <td valign="top" rowspan="6">BR_TV</td>
281 *         <td>BR_TV_L</td>
282 *         <td>Content is suitable for all audiences</td>
283 *     </tr>
284 *     <tr>
285 *         <td>BR_TV_10</td>
286 *         <td>Content suitable for viewers over the age of 10</td>
287 *     </tr>
288 *     <tr>
289 *         <td>BR_TV_12</td>
290 *         <td>Content suitable for viewers over the age of 12</td>
291 *     </tr>
292 *     <tr>
293 *         <td>BR_TV_14</td>
294 *         <td>Content suitable for viewers over the age of 14</td>
295 *     </tr>
296 *     <tr>
297 *         <td>BR_TV_16</td>
298 *         <td>Content suitable for viewers over the age of 16</td>
299 *     </tr>
300 *     <tr>
301 *         <td>BR_TV_18</td>
302 *         <td>Content suitable for viewers over the age of 18</td>
303 *     </tr>
304 *     <tr>
305 *         <td valign="top" rowspan="7">CA_TV_EN</td>
306 *         <td>CA_TV_EN_EXEMPT</td>
307 *         <td>Exempt from ratings</td>
308 *     </tr>
309 *     <tr>
310 *         <td>CA_TV_EN_C</td>
311 *         <td>Suitable for children ages 2&#8211;7</td>
312 *     </tr>
313 *     <tr>
314 *         <td>CA_TV_EN_C8</td>
315 *         <td>Suitable for children ages 8 and older</td>
316 *     </tr>
317 *     <tr>
318 *         <td>CA_TV_EN_G</td>
319 *         <td>Suitable for the entire family</td>
320 *     </tr>
321 *     <tr>
322 *         <td>CA_TV_EN_PG</td>
323 *         <td>May contain moderate violence, profanity, nudity, and sexual references</td>
324 *     </tr>
325 *     <tr>
326 *         <td>CA_TV_EN_14</td>
327 *         <td>Intended for viewers ages 14 and older</td>
328 *     </tr>
329 *     <tr>
330 *         <td>CA_TV_EN_18</td>
331 *         <td>Intended for viewers ages 18 and older</td>
332 *     </tr>
333 *     <tr>
334 *         <td valign="top" rowspan="6">CA_TV_FR</td>
335 *         <td>CA_TV_FR_E</td>
336 *         <td>Exempt from ratings</td>
337 *     </tr>
338 *     <tr>
339 *         <td>CA_TV_FR_G</td>
340 *         <td>Appropriate for all ages</td>
341 *     </tr>
342 *     <tr>
343 *         <td>CA_TV_FR_8</td>
344 *         <td>Appropriate for children 8</td>
345 *     </tr>
346 *     <tr>
347 *         <td>CA_TV_FR_13</td>
348 *         <td>Suitable for children 13</td>
349 *     </tr>
350 *     <tr>
351 *         <td>CA_TV_FR_16</td>
352 *         <td>Recommended for children over the age of 16</td>
353 *     </tr>
354 *     <tr>
355 *         <td>CA_TV_FR_18</td>
356 *         <td>Only to be viewed by adults</td>
357 *     </tr>
358 *     <tr>
359 *         <td valign="top" rowspan="15">DVB</td>
360 *         <td>DVB_4</td>
361 *         <td>Recommended for ages 4 and over</td>
362 *     </tr>
363 *     <tr>
364 *         <td>DVB_5</td>
365 *         <td>Recommended for ages 5 and over</td>
366 *     </tr>
367 *     <tr>
368 *         <td>DVB_6</td>
369 *         <td>Recommended for ages 6 and over</td>
370 *     </tr>
371 *     <tr>
372 *         <td>DVB_7</td>
373 *         <td>Recommended for ages 7 and over</td>
374 *     </tr>
375 *     <tr>
376 *         <td>DVB_8</td>
377 *         <td>Recommended for ages 8 and over</td>
378 *     </tr>
379 *     <tr>
380 *         <td>DVB_9</td>
381 *         <td>Recommended for ages 9 and over</td>
382 *     </tr>
383 *     <tr>
384 *         <td>DVB_10</td>
385 *         <td>Recommended for ages 10 and over</td>
386 *     </tr>
387 *     <tr>
388 *         <td>DVB_11</td>
389 *         <td>Recommended for ages 11 and over</td>
390 *     </tr>
391 *     <tr>
392 *         <td>DVB_12</td>
393 *         <td>Recommended for ages 12 and over</td>
394 *     </tr>
395 *     <tr>
396 *         <td>DVB_13</td>
397 *         <td>Recommended for ages 13 and over</td>
398 *     </tr>
399 *     <tr>
400 *         <td>DVB_14</td>
401 *         <td>Recommended for ages 14 and over</td>
402 *     </tr>
403 *     <tr>
404 *         <td>DVB_15</td>
405 *         <td>Recommended for ages 15 and over</td>
406 *     </tr>
407 *     <tr>
408 *         <td>DVB_16</td>
409 *         <td>Recommended for ages 16 and over</td>
410 *     </tr>
411 *     <tr>
412 *         <td>DVB_17</td>
413 *         <td>Recommended for ages 17 and over</td>
414 *     </tr>
415 *     <tr>
416 *         <td>DVB_18</td>
417 *         <td>Recommended for ages 18 and over</td>
418 *     </tr>
419 *     <tr>
420 *         <td valign="top" rowspan="18">ES_DVB</td>
421 *         <td>ES_DVB_ALL</td>
422 *         <td>Recommended for all ages</td>
423 *     </tr>
424 *     <tr>
425 *         <td>ES_DVB_C</td>
426 *         <td>Recommended for children</td>
427 *     </tr>
428 *     <tr>
429 *         <td>ES_DVB_X</td>
430 *         <td>Recommended for adults</td>
431 *     </tr>
432 *     <tr>
433 *         <td>ES_DVB_4</td>
434 *         <td>Recommended for ages 4 and over</td>
435 *     </tr>
436 *     <tr>
437 *         <td>ES_DVB_5</td>
438 *         <td>Recommended for ages 5 and over</td>
439 *     </tr>
440 *     <tr>
441 *         <td>ES_DVB_6</td>
442 *         <td>Recommended for ages 6 and over</td>
443 *     </tr>
444 *     <tr>
445 *         <td>ES_DVB_7</td>
446 *         <td>Recommended for ages 7 and over</td>
447 *     </tr>
448 *     <tr>
449 *         <td>ES_DVB_8</td>
450 *         <td>Recommended for ages 8 and over</td>
451 *     </tr>
452 *     <tr>
453 *         <td>ES_DVB_9</td>
454 *         <td>Recommended for ages 9 and over</td>
455 *     </tr>
456 *     <tr>
457 *         <td>ES_DVB_10</td>
458 *         <td>Recommended for ages 10 and over</td>
459 *     </tr>
460 *     <tr>
461 *         <td>ES_DVB_11</td>
462 *         <td>Recommended for ages 11 and over</td>
463 *     </tr>
464 *     <tr>
465 *         <td>ES_DVB_12</td>
466 *         <td>Recommended for ages 12 and over</td>
467 *     </tr>
468 *     <tr>
469 *         <td>ES_DVB_13</td>
470 *         <td>Recommended for ages 13 and over</td>
471 *     </tr>
472 *     <tr>
473 *         <td>ES_DVB_14</td>
474 *         <td>Recommended for ages 14 and over</td>
475 *     </tr>
476 *     <tr>
477 *         <td>ES_DVB_15</td>
478 *         <td>Recommended for ages 15 and over</td>
479 *     </tr>
480 *     <tr>
481 *         <td>ES_DVB_16</td>
482 *         <td>Recommended for ages 16 and over</td>
483 *     </tr>
484 *     <tr>
485 *         <td>ES_DVB_17</td>
486 *         <td>Recommended for ages 17 and over</td>
487 *     </tr>
488 *     <tr>
489 *         <td>ES_DVB_18</td>
490 *         <td>Recommended for ages 18 and over</td>
491 *     </tr>
492 *     <tr>
493 *         <td valign="top" rowspan="16">FR_DVB</td>
494 *         <td>FR_DVB_U</td>
495 *         <td>Recommended for all ages</td>
496 *     </tr>
497 *     <tr>
498 *         <td>FR_DVB_4</td>
499 *         <td>Recommended for ages 4 and over</td>
500 *     </tr>
501 *     <tr>
502 *         <td>FR_DVB_5</td>
503 *         <td>Recommended for ages 5 and over</td>
504 *     </tr>
505 *     <tr>
506 *         <td>FR_DVB_6</td>
507 *         <td>Recommended for ages 6 and over</td>
508 *     </tr>
509 *     <tr>
510 *         <td>FR_DVB_7</td>
511 *         <td>Recommended for ages 7 and over</td>
512 *     </tr>
513 *     <tr>
514 *         <td>FR_DVB_8</td>
515 *         <td>Recommended for ages 8 and over</td>
516 *     </tr>
517 *     <tr>
518 *         <td>FR_DVB_9</td>
519 *         <td>Recommended for ages 9 and over</td>
520 *     </tr>
521 *     <tr>
522 *         <td>FR_DVB_10</td>
523 *         <td>Recommended for ages 10 and over</td>
524 *     </tr>
525 *     <tr>
526 *         <td>FR_DVB_11</td>
527 *         <td>Recommended for ages 11 and over</td>
528 *     </tr>
529 *     <tr>
530 *         <td>FR_DVB_12</td>
531 *         <td>Recommended for ages 12 and over</td>
532 *     </tr>
533 *     <tr>
534 *         <td>FR_DVB_13</td>
535 *         <td>Recommended for ages 13 and over</td>
536 *     </tr>
537 *     <tr>
538 *         <td>FR_DVB_14</td>
539 *         <td>Recommended for ages 14 and over</td>
540 *     </tr>
541 *     <tr>
542 *         <td>FR_DVB_15</td>
543 *         <td>Recommended for ages 15 and over</td>
544 *     </tr>
545 *     <tr>
546 *         <td>FR_DVB_16</td>
547 *         <td>Recommended for ages 16 and over</td>
548 *     </tr>
549 *     <tr>
550 *         <td>FR_DVB_17</td>
551 *         <td>Recommended for ages 17 and over</td>
552 *     </tr>
553 *     <tr>
554 *         <td>FR_DVB_18</td>
555 *         <td>Recommended for ages 18 and over</td>
556 *     </tr>
557 *     <tr>
558 *         <td valign="top" rowspan="17">ISDB</td>
559 *         <td>ISDB_4</td>
560 *         <td>Recommended for ages 4 and over</td>
561 *     </tr>
562 *     <tr>
563 *         <td>ISDB_5</td>
564 *         <td>Recommended for ages 5 and over</td>
565 *     </tr>
566 *     <tr>
567 *         <td>ISDB_6</td>
568 *         <td>Recommended for ages 6 and over</td>
569 *     </tr>
570 *     <tr>
571 *         <td>ISDB_7</td>
572 *         <td>Recommended for ages 7 and over</td>
573 *     </tr>
574 *     <tr>
575 *         <td>ISDB_8</td>
576 *         <td>Recommended for ages 8 and over</td>
577 *     </tr>
578 *     <tr>
579 *         <td>ISDB_9</td>
580 *         <td>Recommended for ages 9 and over</td>
581 *     </tr>
582 *     <tr>
583 *         <td>ISDB_10</td>
584 *         <td>Recommended for ages 10 and over</td>
585 *     </tr>
586 *     <tr>
587 *         <td>ISDB_11</td>
588 *         <td>Recommended for ages 11 and over</td>
589 *     </tr>
590 *     <tr>
591 *         <td>ISDB_12</td>
592 *         <td>Recommended for ages 12 and over</td>
593 *     </tr>
594 *     <tr>
595 *         <td>ISDB_13</td>
596 *         <td>Recommended for ages 13 and over</td>
597 *     </tr>
598 *     <tr>
599 *         <td>ISDB_14</td>
600 *         <td>Recommended for ages 14 and over</td>
601 *     </tr>
602 *     <tr>
603 *         <td>ISDB_15</td>
604 *         <td>Recommended for ages 15 and over</td>
605 *     </tr>
606 *     <tr>
607 *         <td>ISDB_16</td>
608 *         <td>Recommended for ages 16 and over</td>
609 *     </tr>
610 *     <tr>
611 *         <td>ISDB_17</td>
612 *         <td>Recommended for ages 17 and over</td>
613 *     </tr>
614 *     <tr>
615 *         <td>ISDB_18</td>
616 *         <td>Recommended for ages 18 and over</td>
617 *     </tr>
618 *     <tr>
619 *         <td>ISDB_19</td>
620 *         <td>Recommended for ages 19 and over</td>
621 *     </tr>
622 *     <tr>
623 *         <td>ISDB_20</td>
624 *         <td>Recommended for ages 20 and over</td>
625 *     </tr>
626 *     <tr>
627 *         <td valign="top" rowspan="5">KR_TV</td>
628 *         <td>KR_TV_ALL</td>
629 *         <td>Appropriate for all ages</td>
630 *     </tr>
631 *     <tr>
632 *         <td>KR_TV_7</td>
633 *         <td>May contain material inappropriate for children younger than 7, and parental
634 *         discretion should be used</td>
635 *     </tr>
636 *     <tr>
637 *         <td>KR_TV_12</td>
638 *         <td>May deemed inappropriate for those younger than 12, and parental discretion should be
639 *         used</td>
640 *     </tr>
641 *     <tr>
642 *         <td>KR_TV_15</td>
643 *         <td>May be inappropriate for children under 15, and that parental discretion should be
644 *         used</td>
645 *     </tr>
646 *     <tr>
647 *         <td>KR_TV_19</td>
648 *         <td>For adults only</td>
649 *     </tr>
650 *     <tr>
651 *         <td valign="top" rowspan="6">SG_TV</td>
652 *         <td>SG_TV_G</td>
653 *         <td>Suitable for all ages</td>
654 *     </tr>
655 *     <tr>
656 *         <td>SG_TV_PG</td>
657 *         <td>Suitable for all but parents should guide their young</td>
658 *     </tr>
659 *     <tr>
660 *         <td>SG_TV_PG13</td>
661 *         <td>Suitable for persons aged 13 and above but parental guidance is advised for children
662 *         below 13</td>
663 *     </tr>
664 *     <tr>
665 *         <td>SG_TV_NC16</td>
666 *         <td>Suitable for persons aged 16 and above</td>
667 *     </tr>
668 *     <tr>
669 *         <td>SG_TV_M18</td>
670 *         <td>Suitable for persons aged 18 and above</td>
671 *     </tr>
672 *     <tr>
673 *         <td>SG_TV_R21</td>
674 *         <td>Suitable for adults aged 21 and above</td>
675 *     </tr>
676 *     <tr>
677 *         <td valign="top" rowspan="5">US_MV</td>
678 *         <td>US_MV_G</td>
679 *         <td>General audiences</td>
680 *     </tr>
681 *     <tr>
682 *         <td>US_MV_PG</td>
683 *         <td>Parental guidance suggested</td>
684 *     </tr>
685 *     <tr>
686 *         <td>US_MV_PG13</td>
687 *         <td>Parents strongly cautioned</td>
688 *     </tr>
689 *     <tr>
690 *         <td>US_MV_R</td>
691 *         <td>Restricted, under 17 requires accompanying parent or adult guardian</td>
692 *     </tr>
693 *     <tr>
694 *         <td>US_MV_NC17</td>
695 *         <td>No one 17 and under admitted</td>
696 *     </tr>
697 *     <tr>
698 *         <td valign="top" rowspan="6">US_TV</td>
699 *         <td>US_TV_Y</td>
700 *         <td>This program is designed to be appropriate for all children</td>
701 *     </tr>
702 *     <tr>
703 *         <td>US_TV_Y7</td>
704 *         <td>This program is designed for children age 7 and above</td>
705 *     </tr>
706 *     <tr>
707 *         <td>US_TV_G</td>
708 *         <td>Most parents would find this program suitable for all ages</td>
709 *     </tr>
710 *     <tr>
711 *         <td>US_TV_PG</td>
712 *         <td>This program contains material that parents may find unsuitable for younger children
713 *         </td>
714 *     </tr>
715 *     <tr>
716 *         <td>US_TV_14</td>
717 *         <td>This program contains some material that many parents would find unsuitable for
718 *         children under 14 years of age</td>
719 *     </tr>
720 *     <tr>
721 *         <td>US_TV_MA</td>
722 *         <td>This program is specifically designed to be viewed by adults and therefore may be
723 *         unsuitable for children under 17</td>
724 *     </tr>
725 * </table>
726 *
727 * <h4>System defined strings for sub-ratings</h4>
728 * <table>
729 *     <tr>
730 *         <th>Rating System</th>
731 *         <th>Constant Value</th>
732 *         <th>Description</th>
733 *     </tr>
734 *     <tr>
735 *         <td valign="top" rowspan="3">BR_TV</td>
736 *         <td>BR_TV_D</td>
737 *         <td>Drugs<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and BR_TV_18
738 *         </td>
739 *     </tr>
740 *     <tr>
741 *         <td>BR_TV_S</td>
742 *         <td>Sex<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and BR_TV_18
743 *         </td>
744 *     </tr>
745 *     <tr>
746 *         <td>BR_TV_V</td>
747 *         <td>Violence<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and
748 *         BR_TV_18</td>
749 *     </tr>
750 *     <tr>
751 *         <td valign="top" rowspan="5">US_TV</td>
752 *         <td>US_TV_D</td>
753 *         <td>Suggestive dialogue (Usually means talks about sex)<br/>Applicable to US_TV_PG, and
754 *         US_TV_14</td>
755 *     </tr>
756 *     <tr>
757 *         <td>US_TV_L</td>
758 *         <td>Coarse language<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
759 *     </tr>
760 *     <tr>
761 *         <td>US_TV_S</td>
762 *         <td>Sexual content<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
763 *     </tr>
764 *     <tr>
765 *         <td>US_TV_V</td>
766 *         <td>Violence<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
767 *     </tr>
768 *     <tr>
769 *         <td>US_TV_FV</td>
770 *         <td>Fantasy violence (Children's programming only)<br/>Applicable to US_TV_Y7</td>
771 *     </tr>
772 * </table>
773 */
774public final class TvContentRating {
775    // TODO: Consider to use other DELIMITER. In some countries such as India may use this delimiter
776    // in the main ratings.
777    private static final String DELIMITER = "/";
778
779    private final String mDomain;
780    private final String mRatingSystem;
781    private final String mRating;
782    private final String[] mSubRatings;
783    private final int mHashCode;
784
785    /**
786     * Rating constant denoting unrated content. Used to handle the case where the content rating
787     * information is missing.
788     *
789     * <p>TV input services can call {@link TvInputManager#isRatingBlocked} with this constant to
790     * determine whether they should block unrated content. The subsequent call to
791     * {@link TvInputService.Session#notifyContentBlocked} with the same constant notifies
792     * applications that the current program content is blocked by parental controls.
793     */
794    public static final TvContentRating UNRATED = new TvContentRating("null", "null", "null", null);
795
796    /**
797     * Creates a {@code TvContentRating} object with predefined content rating strings.
798     *
799     * @param domain The domain string. For example, "com.android.tv".
800     * @param ratingSystem The rating system string. For example, "US_TV".
801     * @param rating The content rating string. For example, "US_TV_PG".
802     * @param subRatings The sub-rating strings. For example, "US_TV_D" and "US_TV_L".
803     * @return A {@code TvContentRating} object.
804     * @throws IllegalArgumentException If {@code domain}, {@code ratingSystem} or {@code rating} is
805     *             {@code null}.
806     */
807    public static TvContentRating createRating(String domain, String ratingSystem,
808            String rating, String... subRatings) {
809        if (TextUtils.isEmpty(domain)) {
810            throw new IllegalArgumentException("domain cannot be empty");
811        }
812        if (TextUtils.isEmpty(ratingSystem)) {
813            throw new IllegalArgumentException("ratingSystem cannot be empty");
814        }
815        if (TextUtils.isEmpty(rating)) {
816            throw new IllegalArgumentException("rating cannot be empty");
817        }
818        return new TvContentRating(domain, ratingSystem, rating, subRatings);
819    }
820
821    /**
822     * Recovers a {@code TvContentRating} object from the string that was previously created from
823     * {@link #flattenToString}.
824     *
825     * @param ratingString The string returned by {@link #flattenToString}.
826     * @return the {@code TvContentRating} object containing the domain, rating system, rating and
827     *         sub-ratings information encoded in {@code ratingString}.
828     * @see #flattenToString
829     */
830    public static TvContentRating unflattenFromString(String ratingString) {
831        if (TextUtils.isEmpty(ratingString)) {
832            throw new IllegalArgumentException("ratingString cannot be empty");
833        }
834        String[] strs = ratingString.split(DELIMITER);
835        if (strs.length < 3) {
836            throw new IllegalArgumentException("Invalid rating string: " + ratingString);
837        }
838        if (strs.length > 3) {
839            String[] subRatings = new String[strs.length - 3];
840            System.arraycopy(strs, 3, subRatings, 0, subRatings.length);
841            return new TvContentRating(strs[0], strs[1], strs[2], subRatings);
842        }
843        return new TvContentRating(strs[0], strs[1], strs[2], null);
844    }
845
846    /**
847     * Constructs a TvContentRating object from a given rating and sub-rating constants.
848     *
849     * @param domain The string for domain of the content rating system such as "com.android.tv".
850     * @param ratingSystem The rating system string such as "US_TV".
851     * @param rating The content rating string such as "US_TV_PG".
852     * @param subRatings The sub-rating strings such as "US_TV_D" and "US_TV_L".
853     */
854    private TvContentRating(
855            String domain, String ratingSystem, String rating, String[] subRatings) {
856        mDomain = domain;
857        mRatingSystem = ratingSystem;
858        mRating = rating;
859        if (subRatings == null || subRatings.length == 0) {
860            mSubRatings = null;
861        } else {
862            Arrays.sort(subRatings);
863            mSubRatings = subRatings;
864        }
865        mHashCode = 31 * Objects.hash(mDomain, mRating) + Arrays.hashCode(mSubRatings);
866    }
867
868    /**
869     * Returns the domain of this {@code TvContentRating} object.
870     */
871    public String getDomain() {
872        return mDomain;
873    }
874
875    /**
876     * Returns the rating system of this {@code TvContentRating} object.
877     */
878    public String getRatingSystem() {
879        return mRatingSystem;
880    }
881
882    /**
883     * Returns the main rating of this {@code TvContentRating} object.
884     */
885    public String getMainRating() {
886        return mRating;
887    }
888
889    /**
890     * Returns the unmodifiable sub-rating string {@link List} of this {@code TvContentRating}
891     * object.
892     */
893    public List<String> getSubRatings() {
894        if (mSubRatings == null) {
895            return null;
896        }
897        return Collections.unmodifiableList(Arrays.asList(mSubRatings));
898    }
899
900    /**
901     * Returns a string that unambiguously describes the rating information contained in a
902     * {@code TvContentRating} object. One can later recover the object from this string through
903     * {@link #unflattenFromString}.
904     *
905     * @return a string containing the rating information, which can later be stored in the
906     *         database.
907     * @see #unflattenFromString
908     */
909    public String flattenToString() {
910        StringBuilder builder = new StringBuilder();
911        builder.append(mDomain);
912        builder.append(DELIMITER);
913        builder.append(mRatingSystem);
914        builder.append(DELIMITER);
915        builder.append(mRating);
916        if (mSubRatings != null) {
917            for (String subRating : mSubRatings) {
918                builder.append(DELIMITER);
919                builder.append(subRating);
920            }
921        }
922        return builder.toString();
923    }
924
925    /**
926     * Returns {@code true} if this rating has the same main rating as the specified rating and when
927     * this rating's sub-ratings contain the other's.
928     *
929     * <p>For example, a {@code TvContentRating} object that represents TV-PG with
930     * S(Sexual content) and V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
931     *
932     * @param rating The {@link TvContentRating} to check.
933     * @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
934     */
935    public final boolean contains(@NonNull TvContentRating rating) {
936        Preconditions.checkNotNull(rating);
937        if (!rating.getMainRating().equals(mRating)) {
938            return false;
939        }
940        if (!rating.getDomain().equals(mDomain) ||
941                !rating.getRatingSystem().equals(mRatingSystem) ||
942                !rating.getMainRating().equals(mRating)) {
943            return false;
944        }
945        List<String> subRatings = getSubRatings();
946        List<String> subRatingsOther = rating.getSubRatings();
947        if (subRatings == null && subRatingsOther == null) {
948            return true;
949        } else if (subRatings == null && subRatingsOther != null) {
950            return false;
951        } else if (subRatings != null && subRatingsOther == null) {
952            return true;
953        } else {
954            return subRatings.containsAll(subRatingsOther);
955        }
956    }
957
958    @Override
959    public boolean equals(Object obj) {
960        if (!(obj instanceof TvContentRating)) {
961            return false;
962        }
963        TvContentRating other = (TvContentRating) obj;
964        if (mHashCode != other.mHashCode) {
965            return false;
966        }
967        if (!TextUtils.equals(mDomain, other.mDomain)) {
968            return false;
969        }
970        if (!TextUtils.equals(mRatingSystem, other.mRatingSystem)) {
971            return false;
972        }
973        if (!TextUtils.equals(mRating, other.mRating)) {
974            return false;
975        }
976        return Arrays.equals(mSubRatings, other.mSubRatings);
977    }
978
979    @Override
980    public int hashCode() {
981        return mHashCode;
982    }
983}
984