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