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