TvContentRating.java revision 6057102dbb746593a7d59cf377c969b62e38c664
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;
21import android.util.ArrayMap;
22import android.util.Log;
23
24import java.util.Arrays;
25import java.util.Collections;
26import java.util.List;
27import java.util.Map;
28
29/**
30 * A class representing a TV content rating.
31 */
32public final class TvContentRating {
33    private static final String TAG = "TvContentRating";
34
35    private static final int RATING_PREFIX_LENGTH = 10;
36    private static final String PREFIX_RATING_US = "RATING_US_";
37    private static final String PREFIX_SUBRATING_US = "SUBRATING_US_";
38
39    /**
40     * Rating constant for TV-Y from the TV Parental Guidelines system in US. This program is
41     * designed to be appropriate for all children.
42     */
43    public static final String RATING_US_TV_Y = PREFIX_RATING_US + "TV_Y";
44    /**
45     * Rating constant for TV-Y7 from the TV Parental Guidelines system in US. This program is
46     * designed for children age 7 and above.
47     */
48    public static final String RATING_US_TV_Y7 = PREFIX_RATING_US + "TV_Y7";
49    /**
50     * Rating constant for TV-G from the TV Parental Guidelines system in US. Most parents would
51     * find this program suitable for all ages.
52     */
53    public static final String RATING_US_TV_G = PREFIX_RATING_US + "TV_G";
54    /**
55     * Rating constant for TV-PG from the TV Parental Guidelines system in US. This program contains
56     * material that parents may find unsuitable for younger children.
57     */
58    public static final String RATING_US_TV_PG = PREFIX_RATING_US + "TV_PG";
59    /**
60     * Rating constant for TV-14 from the TV Parental Guidelines system in US. This program contains
61     * some material that many parents would find unsuitable for children under 14 years of age.
62     */
63    public static final String RATING_US_TV_14 = PREFIX_RATING_US + "TV_14";
64    /**
65     * Rating constant for TV-MA from the TV Parental Guidelines system in US. This program is
66     * specifically designed to be viewed by adults and therefore may be unsuitable for children
67     * under 17.
68     */
69    public static final String RATING_US_TV_MA = PREFIX_RATING_US + "TV_MA";
70
71    /**
72     * Sub-rating constant for D (Suggestive dialogue) from the TV Parental Guidelines system in US.
73     */
74    public static final String SUBRATING_US_D = PREFIX_SUBRATING_US + "D";
75    /**
76     * Sub-rating constant for L (Coarse language) from the TV Parental Guidelines system in US.
77     */
78    public static final String SUBRATING_US_L = PREFIX_SUBRATING_US + "L";
79    /**
80     * Sub-rating constant for S (Sexual content) from the TV Parental Guidelines system in US.
81     */
82    public static final String SUBRATING_US_S = PREFIX_SUBRATING_US + "S";
83    /**
84     * Sub-rating constant for V (Violence) from the TV Parental Guidelines system in US.
85     */
86    public static final String SUBRATING_US_V = PREFIX_SUBRATING_US + "V";
87    /**
88     * Sub-rating constant for FV (Fantasy violence) from the TV Parental Guidelines system in US.
89     */
90    public static final String SUBRATING_US_FV = PREFIX_SUBRATING_US + "FV";
91
92    private static final String PREFIX_RATING_KR = "RATING_KR_";
93
94    /**
95     * Rating constant for 'ALL' from the South Korean television rating system. This rating is for
96     * programming that is appropriate for all ages.
97     */
98    public static final String RATING_KR_ALL = PREFIX_RATING_KR + "ALL";
99    /**
100     * Rating constant for '7' from the South Korean television rating system. This rating is for
101     * programming that may contain material inappropriate for children younger than 7, and parental
102     * discretion should be used.
103     */
104    public static final String RATING_KR_7 = PREFIX_RATING_KR + "7";
105    /**
106     * Rating constant for '12' from the South Korean television rating system. This rating is for
107     * programs that may deemed inappropriate for those younger than 12, and parental discretion
108     * should be used.
109     */
110    public static final String RATING_KR_12 = PREFIX_RATING_KR + "12";
111    /**
112     * Rating constant for '15' from the South Korean television rating system. This rating is for
113     * programs that contain material that may be inappropriate for children under 15, and that
114     * parental discretion should be used.
115     */
116    public static final String RATING_KR_15 = PREFIX_RATING_KR + "15";
117    /**
118     * Rating constant for '19' from the South Korean television rating system. This rating is for
119     * programs that are intended for adults only. 19-rated programming cannot air during the hours
120     * of 7:00AM to 9:00AM, and 1:00PM to 10:00PM.
121     */
122    public static final String RATING_KR_19 = PREFIX_RATING_KR + "19";
123
124    private static final String DELIMITER = "/";
125
126    // A mapping from two-letter country code (ISO 3166-1 alpha-2) to its rating-to-sub-ratings map.
127    // This is used for validating the builder parameters.
128    private static final Map<String, Map<String, String[]>> sRatings
129            = new ArrayMap<String, Map<String, String[]>>();
130
131    static {
132        Map<String, String[]> usRatings = new ArrayMap<String, String[]>();
133        usRatings.put(RATING_US_TV_Y, null);
134        usRatings.put(RATING_US_TV_Y7, new String[] { SUBRATING_US_FV });
135        usRatings.put(RATING_US_TV_G, null);
136        usRatings.put(RATING_US_TV_PG, new String[] {
137                SUBRATING_US_D, SUBRATING_US_L, SUBRATING_US_S, SUBRATING_US_V });
138        usRatings.put(RATING_US_TV_14, new String[] {
139                SUBRATING_US_D, SUBRATING_US_L, SUBRATING_US_S, SUBRATING_US_V });
140        usRatings.put(RATING_US_TV_MA, new String[] {
141                SUBRATING_US_L, SUBRATING_US_S, SUBRATING_US_V });
142        sRatings.put(PREFIX_RATING_US, usRatings);
143
144        Map<String, String[]> krRatings = new ArrayMap<String, String[]>();
145        krRatings.put(RATING_KR_ALL, null);
146        krRatings.put(RATING_KR_7, null);
147        krRatings.put(RATING_KR_12, null);
148        krRatings.put(RATING_KR_15, null);
149        krRatings.put(RATING_KR_19, null);
150        sRatings.put(PREFIX_RATING_KR, krRatings);
151    }
152
153    private final String mRating;
154    private final String[] mSubRatings;
155
156    /**
157     * Constructs a TvContentRating object from a given rating constant.
158     *
159     * @param rating The rating constant defined in this class.
160     */
161    public TvContentRating(String rating) {
162        this(rating, null);
163    }
164
165    /**
166     * Constructs a TvContentRating object from a given rating and sub-rating constants.
167     *
168     * @param rating The rating constant defined in this class.
169     * @param subRatings The String array of sub-rating constants defined in this class.
170     */
171    public TvContentRating(String rating, String[] subRatings) {
172        if (TextUtils.isEmpty(rating)) {
173            throw new IllegalArgumentException("rating cannot be null");
174        }
175        mRating = rating;
176        mSubRatings = subRatings;
177        String prefix = "";
178        if (mRating.length() > RATING_PREFIX_LENGTH) {
179            prefix = mRating.substring(0, RATING_PREFIX_LENGTH);
180        }
181        Map<String, String[]> ratings = sRatings.get(prefix);
182        if (ratings != null) {
183            if (!ratings.keySet().contains(mRating)) {
184                Log.w(TAG, "Unknown rating: " + mRating);
185            } else if (mSubRatings != null) {
186                String[] validSubRatings = ratings.get(mRating);
187                if (validSubRatings == null) {
188                    Log.w(TAG, "Invalid subratings: " + mSubRatings);
189                } else {
190                    List<String> validSubRatingList = Arrays.asList(subRatings);
191                    for (String sr : mSubRatings) {
192                        if (TextUtils.isEmpty(sr)) {
193                            throw new IllegalArgumentException(
194                                    "subRatings cannot contain empty elements");
195                        }
196                        if (!validSubRatingList.contains(sr)) {
197                            Log.w(TAG, "Invalid subrating: " + sr);
198                            break;
199                        }
200                    }
201                }
202            }
203        } else {
204            Log.w(TAG, "Rating undefined for " + mRating);
205        }
206    }
207
208    /**
209     * Returns the main rating constant.
210     *
211     * @return the rating string that starts with "RATING_" prefix as defined in this class.
212     */
213    public String getMainRating() {
214        return mRating;
215    }
216
217    /**
218     * Returns the list of sub-rating constants.
219     *
220     * @return the unmodifiable {@code List} of sub-rating strings that start with "SUBRATING_"
221     *         prefix as defined in this class.
222     */
223    public List<String> getSubRatings() {
224        if (mSubRatings == null) {
225            return null;
226        }
227        return Collections.unmodifiableList(Arrays.asList(mSubRatings));
228    }
229
230
231    /**
232     * Returns a String that unambiguously describes both the rating and sub-rating information
233     * contained in the TvContentRating. You can later recover the TvContentRating from this string
234     * through {@link #unflattenFromString}.
235     *
236     * @return a new String holding rating/sub-rating information, which can later be stored in the
237     *         database and settings.
238     * @see #unflattenFromString
239     */
240    public String flattenToString() {
241        // TODO: Consider removing all obvious/redundant sub-strings including "RATING" and
242        // "SUBRATING" and find out a storage-efficient string format such as:
243        // <country>-<primary>/<sub1>/<sub2>/<sub3>
244        StringBuilder builder = new StringBuilder(mRating);
245        if (mSubRatings != null) {
246            for (String subRating : mSubRatings) {
247                builder.append(DELIMITER);
248                builder.append(subRating);
249            }
250        }
251        return builder.toString();
252    }
253
254    /**
255     * Recovers a TvContentRating from a String that was previously created with
256     * {@link #flattenToString}.
257     *
258     * @param ratingString The String that was returned by flattenToString().
259     * @return a new TvContentRating containing the rating and sub-ratings information was encoded
260     *         in {@code ratingString}.
261     * @see #flattenToString
262     */
263    public static TvContentRating unflattenFromString(String ratingString) {
264        if (TextUtils.isEmpty(ratingString)) {
265            throw new IllegalArgumentException("Empty rating string");
266        }
267        String[] strs = ratingString.split(DELIMITER);
268        if (strs.length < 1) {
269            throw new IllegalArgumentException("Invalid rating string: " + ratingString);
270        }
271        if (strs.length > 1) {
272            String[] subRatings = new String[strs.length - 1];
273            System.arraycopy(strs, 1, subRatings, 0, subRatings.length);
274            return new TvContentRating(strs[0], subRatings);
275        }
276        return new TvContentRating(strs[0]);
277    }
278
279    /**
280     * Returns true if this rating has the same main rating as the specified rating and when this
281     * rating's sub-ratings contain the other's.
282     * <p>
283     * For example, a TvContentRating object that represents TV-PG with S(Sexual content) and
284     * V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
285     * </p>
286     *
287     * @param rating The {@link TvContentRating} to check.
288     * @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
289     * @hide
290     */
291    @SystemApi
292    public final boolean contains(TvContentRating rating) {
293        if (rating == null) {
294            throw new IllegalArgumentException("rating cannot be null");
295        }
296        if (!rating.getMainRating().equals(mRating)) {
297            return false;
298        }
299        List<String> subRatings = getSubRatings();
300        List<String> subRatingsOther = rating.getSubRatings();
301        if (subRatings == null && subRatingsOther == null) {
302            return true;
303        } else if (subRatings == null && subRatingsOther != null) {
304            return false;
305        } else if (subRatings != null && subRatingsOther == null) {
306            return true;
307        } else {
308            return subRatings.containsAll(subRatingsOther);
309        }
310    }
311}
312