1/* 2 * Copyright (C) 2015 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 com.android.tv.parental; 18 19import android.content.Context; 20import android.media.tv.TvContentRating; 21import android.media.tv.TvInputManager; 22 23import com.android.tv.experiments.Experiments; 24import com.android.tv.parental.ContentRatingSystem.Rating; 25import com.android.tv.parental.ContentRatingSystem.SubRating; 26import com.android.tv.util.TvSettings; 27import com.android.tv.util.TvSettings.ContentRatingLevel; 28 29import java.util.HashSet; 30import java.util.Set; 31 32public class ParentalControlSettings { 33 /** 34 * The rating and all of its sub-ratings are blocked. 35 */ 36 public static final int RATING_BLOCKED = 0; 37 38 /** 39 * The rating is blocked but not all of its sub-ratings are blocked. 40 */ 41 public static final int RATING_BLOCKED_PARTIAL = 1; 42 43 /** 44 * The rating is not blocked. 45 */ 46 public static final int RATING_NOT_BLOCKED = 2; 47 48 private final Context mContext; 49 private final TvInputManager mTvInputManager; 50 51 // mRatings is expected to be synchronized with mTvInputManager.getBlockedRatings(). 52 private Set<TvContentRating> mRatings; 53 private Set<TvContentRating> mCustomRatings; 54 55 public ParentalControlSettings(Context context) { 56 mContext = context; 57 mTvInputManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE); 58 } 59 60 public boolean isParentalControlsEnabled() { 61 return mTvInputManager.isParentalControlsEnabled(); 62 } 63 64 public void setParentalControlsEnabled(boolean enabled) { 65 mTvInputManager.setParentalControlsEnabled(enabled); 66 } 67 68 public void setContentRatingSystemEnabled(ContentRatingsManager manager, 69 ContentRatingSystem contentRatingSystem, boolean enabled) { 70 if (enabled) { 71 TvSettings.addContentRatingSystem(mContext, contentRatingSystem.getId()); 72 73 // Ensure newly added system has ratings for current level set 74 updateRatingsForCurrentLevel(manager); 75 } else { 76 // Ensure no ratings are blocked for the selected rating system 77 for (TvContentRating tvContentRating : mTvInputManager.getBlockedRatings()) { 78 if (contentRatingSystem.ownsRating(tvContentRating)) { 79 mTvInputManager.removeBlockedRating(tvContentRating); 80 } 81 } 82 83 TvSettings.removeContentRatingSystem(mContext, contentRatingSystem.getId()); 84 } 85 } 86 87 public boolean isContentRatingSystemEnabled(ContentRatingSystem contentRatingSystem) { 88 return TvSettings.hasContentRatingSystem(mContext, contentRatingSystem.getId()); 89 } 90 91 public void loadRatings() { 92 mRatings = new HashSet<>(mTvInputManager.getBlockedRatings()); 93 } 94 95 private void storeRatings() { 96 Set<TvContentRating> removed = new HashSet<>(mTvInputManager.getBlockedRatings()); 97 removed.removeAll(mRatings); 98 for (TvContentRating tvContentRating : removed) { 99 mTvInputManager.removeBlockedRating(tvContentRating); 100 } 101 102 Set<TvContentRating> added = new HashSet<>(mRatings); 103 added.removeAll(mTvInputManager.getBlockedRatings()); 104 for (TvContentRating tvContentRating : added) { 105 mTvInputManager.addBlockedRating(tvContentRating); 106 } 107 } 108 109 private void updateRatingsForCurrentLevel(ContentRatingsManager manager) { 110 @ContentRatingLevel int currentLevel = getContentRatingLevel(); 111 if (currentLevel != TvSettings.CONTENT_RATING_LEVEL_CUSTOM) { 112 mRatings = ContentRatingLevelPolicy.getRatingsForLevel(this, manager, currentLevel); 113 if (currentLevel != TvSettings.CONTENT_RATING_LEVEL_NONE) { 114 // UNRATED contents should be blocked unless the rating level is none or custom 115 mRatings.add(TvContentRating.UNRATED); 116 } 117 storeRatings(); 118 } 119 } 120 121 public void setContentRatingLevel(ContentRatingsManager manager, 122 @ContentRatingLevel int level) { 123 @ContentRatingLevel int currentLevel = getContentRatingLevel(); 124 if (level == currentLevel) { 125 return; 126 } 127 if (currentLevel == TvSettings.CONTENT_RATING_LEVEL_CUSTOM) { 128 mCustomRatings = mRatings; 129 } 130 TvSettings.setContentRatingLevel(mContext, level); 131 if (level == TvSettings.CONTENT_RATING_LEVEL_CUSTOM) { 132 if (mCustomRatings != null) { 133 mRatings = new HashSet<>(mCustomRatings); 134 } 135 } else { 136 mRatings = ContentRatingLevelPolicy.getRatingsForLevel(this, manager, level); 137 if (level != TvSettings.CONTENT_RATING_LEVEL_NONE 138 && Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) { 139 // UNRATED contents should be blocked unless the rating level is none or custom 140 mRatings.add(TvContentRating.UNRATED); 141 } 142 } 143 storeRatings(); 144 } 145 146 @ContentRatingLevel 147 public int getContentRatingLevel() { 148 return TvSettings.getContentRatingLevel(mContext); 149 } 150 151 /** Sets the blocked status of a unrated contents. */ 152 public boolean setUnratedBlocked(boolean blocked) { 153 boolean changed; 154 if (blocked) { 155 changed = mRatings.add(TvContentRating.UNRATED); 156 mTvInputManager.addBlockedRating(TvContentRating.UNRATED); 157 } else { 158 changed = mRatings.remove(TvContentRating.UNRATED); 159 mTvInputManager.removeBlockedRating(TvContentRating.UNRATED); 160 } 161 if (changed) { 162 // change to custom level if the blocked status is changed 163 changeToCustomLevel(); 164 } 165 return changed; 166 } 167 168 /** 169 * Sets the blocked status of a given content rating. 170 * <p> 171 * Note that a call to this method automatically changes the current rating level to 172 * {@code TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed. 173 * </p> 174 * 175 * @param contentRatingSystem The content rating system where the given rating belongs. 176 * @param rating The content rating to set. 177 * @return {@code true} if changed, {@code false} otherwise. 178 * @see #setSubRatingBlocked 179 */ 180 public boolean setRatingBlocked(ContentRatingSystem contentRatingSystem, Rating rating, 181 boolean blocked) { 182 return setRatingBlockedInternal(contentRatingSystem, rating, null, blocked); 183 } 184 185 /** 186 * Checks whether any of given ratings is blocked. 187 * 188 * @param ratings The array of ratings to check 189 * @return {@code true} if a rating is blocked, {@code false} otherwise. 190 */ 191 public boolean isRatingBlocked(TvContentRating[] ratings) { 192 return getBlockedRating(ratings) != null; 193 } 194 195 /** 196 * Checks whether any of given ratings is blocked and returns the first blocked rating. 197 * 198 * @param ratings The array of ratings to check 199 * @return The {@link TvContentRating} that is blocked. 200 */ 201 public TvContentRating getBlockedRating(TvContentRating[] ratings) { 202 if (ratings == null || ratings.length <= 0) { 203 return mTvInputManager.isRatingBlocked(TvContentRating.UNRATED) 204 ? TvContentRating.UNRATED 205 : null; 206 } 207 for (TvContentRating rating : ratings) { 208 if (mTvInputManager.isRatingBlocked(rating)) { 209 return rating; 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Checks whether a given rating is blocked by the user or not. 217 * 218 * @param contentRatingSystem The content rating system where the given rating belongs. 219 * @param rating The content rating to check. 220 * @return {@code true} if blocked, {@code false} otherwise. 221 */ 222 public boolean isRatingBlocked(ContentRatingSystem contentRatingSystem, Rating rating) { 223 return mRatings.contains(toTvContentRating(contentRatingSystem, rating)); 224 } 225 226 /** 227 * Sets the blocked status of a given content sub-rating. 228 * <p> 229 * Note that a call to this method automatically changes the current rating level to 230 * {@code TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed. 231 * </p> 232 * 233 * @param contentRatingSystem The content rating system where the given rating belongs. 234 * @param rating The content rating associated with the given sub-rating. 235 * @param subRating The content sub-rating to set. 236 * @return {@code true} if changed, {@code false} otherwise. 237 * @see #setRatingBlocked 238 */ 239 public boolean setSubRatingBlocked(ContentRatingSystem contentRatingSystem, Rating rating, 240 SubRating subRating, boolean blocked) { 241 return setRatingBlockedInternal(contentRatingSystem, rating, subRating, blocked); 242 } 243 244 /** 245 * Checks whether a given content sub-rating is blocked by the user or not. 246 * 247 * @param contentRatingSystem The content rating system where the given rating belongs. 248 * @param rating The content rating associated with the given sub-rating. 249 * @param subRating The content sub-rating to check. 250 * @return {@code true} if blocked, {@code false} otherwise. 251 */ 252 public boolean isSubRatingEnabled(ContentRatingSystem contentRatingSystem, Rating rating, 253 SubRating subRating) { 254 return mRatings.contains(toTvContentRating(contentRatingSystem, rating, subRating)); 255 } 256 257 private boolean setRatingBlockedInternal(ContentRatingSystem contentRatingSystem, Rating rating, 258 SubRating subRating, boolean blocked) { 259 TvContentRating tvContentRating = (subRating == null) 260 ? toTvContentRating(contentRatingSystem, rating) 261 : toTvContentRating(contentRatingSystem, rating, subRating); 262 boolean changed; 263 if (blocked) { 264 changed = mRatings.add(tvContentRating); 265 mTvInputManager.addBlockedRating(tvContentRating); 266 } else { 267 changed = mRatings.remove(tvContentRating); 268 mTvInputManager.removeBlockedRating(tvContentRating); 269 } 270 if (changed) { 271 changeToCustomLevel(); 272 } 273 return changed; 274 } 275 276 private void changeToCustomLevel() { 277 if (getContentRatingLevel() != TvSettings.CONTENT_RATING_LEVEL_CUSTOM) { 278 TvSettings.setContentRatingLevel(mContext, TvSettings.CONTENT_RATING_LEVEL_CUSTOM); 279 } 280 } 281 282 /** 283 * Returns the blocked status of a given rating. The status can be one of the followings: 284 * {@link #RATING_BLOCKED}, {@link #RATING_BLOCKED_PARTIAL} and {@link #RATING_NOT_BLOCKED} 285 */ 286 public int getBlockedStatus(ContentRatingSystem contentRatingSystem, Rating rating) { 287 if (isRatingBlocked(contentRatingSystem, rating)) { 288 return RATING_BLOCKED; 289 } 290 for (SubRating subRating : rating.getSubRatings()) { 291 if (isSubRatingEnabled(contentRatingSystem, rating, subRating)) { 292 return RATING_BLOCKED_PARTIAL; 293 } 294 } 295 return RATING_NOT_BLOCKED; 296 } 297 298 private TvContentRating toTvContentRating(ContentRatingSystem contentRatingSystem, 299 Rating rating) { 300 return TvContentRating.createRating(contentRatingSystem.getDomain(), 301 contentRatingSystem.getName(), rating.getName()); 302 } 303 304 private TvContentRating toTvContentRating(ContentRatingSystem contentRatingSystem, 305 Rating rating, SubRating subRating) { 306 return TvContentRating.createRating(contentRatingSystem.getDomain(), 307 contentRatingSystem.getName(), rating.getName(), subRating.getName()); 308 } 309} 310