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