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