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