• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ui.sidepanel.parentalcontrols;
18 
19 import android.graphics.drawable.Drawable;
20 import android.media.tv.TvContentRating;
21 import android.os.Bundle;
22 import android.util.ArrayMap;
23 import android.util.SparseIntArray;
24 import android.view.View;
25 import android.widget.CompoundButton;
26 import android.widget.ImageView;
27 import com.android.tv.MainActivity;
28 import com.android.tv.R;
29 import com.android.tv.common.experiments.Experiments;
30 import com.android.tv.dialog.WebDialogFragment;
31 import com.android.tv.license.LicenseUtils;
32 import com.android.tv.parental.ContentRatingSystem;
33 import com.android.tv.parental.ContentRatingSystem.Rating;
34 import com.android.tv.parental.ParentalControlSettings;
35 import com.android.tv.ui.sidepanel.CheckBoxItem;
36 import com.android.tv.ui.sidepanel.DividerItem;
37 import com.android.tv.ui.sidepanel.Item;
38 import com.android.tv.ui.sidepanel.RadioButtonItem;
39 import com.android.tv.ui.sidepanel.SideFragment;
40 import com.android.tv.util.TvSettings;
41 import com.android.tv.util.TvSettings.ContentRatingLevel;
42 import com.google.common.collect.ImmutableList;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Map;
47 
48 public class RatingsFragment extends SideFragment {
49     private static final SparseIntArray sLevelResourceIdMap;
50     private static final SparseIntArray sDescriptionResourceIdMap;
51     private static final String TRACKER_LABEL = "Ratings";
52     private int mItemsSize;
53 
54     static {
55         sLevelResourceIdMap = new SparseIntArray(5);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_NONE, R.string.option_rating_none)56         sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_NONE, R.string.option_rating_none);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high)57         sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high);
sLevelResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium)58         sLevelResourceIdMap.put(
59                 TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low)60         sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low);
sLevelResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom)61         sLevelResourceIdMap.put(
62                 TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom);
63 
64         sDescriptionResourceIdMap = new SparseIntArray(sLevelResourceIdMap.size());
sDescriptionResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high_description)65         sDescriptionResourceIdMap.put(
66                 TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high_description);
sDescriptionResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium_description)67         sDescriptionResourceIdMap.put(
68                 TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium_description);
sDescriptionResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low_description)69         sDescriptionResourceIdMap.put(
70                 TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low_description);
sDescriptionResourceIdMap.put( TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom_description)71         sDescriptionResourceIdMap.put(
72                 TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom_description);
73     }
74 
75     private final List<RatingLevelItem> mRatingLevelItems = new ArrayList<>();
76     // A map from the rating system ID string to RatingItem objects.
77     private final Map<String, List<RatingItem>> mContentRatingSystemItemMap = new ArrayMap<>();
78     private CheckBoxItem mBlockUnratedItem;
79     private ParentalControlSettings mParentalControlSettings;
80 
getDescription(MainActivity tvActivity)81     public static String getDescription(MainActivity tvActivity) {
82         @ContentRatingLevel
83         int currentLevel = tvActivity.getParentalControlSettings().getContentRatingLevel();
84         if (sLevelResourceIdMap.indexOfKey(currentLevel) >= 0) {
85             return tvActivity.getString(sLevelResourceIdMap.get(currentLevel));
86         }
87         return null;
88     }
89 
90     @Override
getTitle()91     protected String getTitle() {
92         return getString(R.string.option_ratings);
93     }
94 
95     @Override
getTrackerLabel()96     public String getTrackerLabel() {
97         return TRACKER_LABEL;
98     }
99 
100     @Override
getItemList()101     protected List<Item> getItemList() {
102         List<Item> items = new ArrayList<>();
103 
104         if (mBlockUnratedItem != null
105                 && Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
106             items.add(mBlockUnratedItem);
107             items.add(new DividerItem());
108         }
109 
110         mRatingLevelItems.clear();
111         for (int i = 0; i < sLevelResourceIdMap.size(); ++i) {
112             mRatingLevelItems.add(new RatingLevelItem(sLevelResourceIdMap.keyAt(i)));
113         }
114         updateRatingLevels();
115         items.addAll(mRatingLevelItems);
116 
117         mContentRatingSystemItemMap.clear();
118 
119         List<ContentRatingSystem> contentRatingSystems =
120                 getMainActivity().getContentRatingsManager().getContentRatingSystems();
121         Collections.sort(contentRatingSystems, ContentRatingSystem.DISPLAY_NAME_COMPARATOR);
122 
123         for (ContentRatingSystem s : contentRatingSystems) {
124             if (mParentalControlSettings.isContentRatingSystemEnabled(s)) {
125                 List<RatingItem> ratingItems = new ArrayList<>();
126                 boolean hasSubRating = false;
127                 items.add(new DividerItem(s.getDisplayName()));
128                 for (Rating rating : s.getRatings()) {
129                     RatingItem item =
130                             rating.getSubRatings().isEmpty()
131                                     ? new RatingItem(s, rating)
132                                     : new RatingWithSubItem(s, rating);
133                     items.add(item);
134                     if (rating.getSubRatings().isEmpty()) {
135                         ratingItems.add(item);
136                     } else {
137                         hasSubRating = true;
138                     }
139                 }
140                 // Only include rating systems that don't contain any sub ratings in the map for
141                 // simplicity.
142                 if (!hasSubRating) {
143                     mContentRatingSystemItemMap.put(s.getId(), ratingItems);
144                 }
145             }
146         }
147         if (LicenseUtils.hasRatingAttribution(getMainActivity().getAssets())) {
148             // Display the attribution if our content rating system is selected.
149             items.add(new DividerItem());
150             items.add(new AttributionItem(getMainActivity()));
151         }
152         mItemsSize = items.size();
153         return items;
154     }
155 
156     @Override
onCreate(Bundle savedInstanceState)157     public void onCreate(Bundle savedInstanceState) {
158         super.onCreate(savedInstanceState);
159         mParentalControlSettings = getMainActivity().getParentalControlSettings();
160         mParentalControlSettings.loadRatings();
161         if (Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
162             mBlockUnratedItem =
163                     new CheckBoxItem(
164                             getResources().getString(R.string.option_block_unrated_programs)) {
165 
166                         @Override
167                         protected void onUpdate() {
168                             super.onUpdate();
169                             setChecked(
170                                     mParentalControlSettings.isRatingBlocked(
171                                             ImmutableList.of(TvContentRating.UNRATED)));
172                         }
173 
174                         @Override
175                         protected void onSelected() {
176                             super.onSelected();
177                             if (mParentalControlSettings.setUnratedBlocked(isChecked())) {
178                                 updateRatingLevels();
179                             }
180                         }
181                     };
182         }
183     }
184 
185     @Override
onResume()186     public void onResume() {
187         super.onResume();
188         // Although we set the attribution item at the end of the item list non-focusable, we do get
189         // its position when the fragment is resumed. This ensures that we do not select the
190         // non-focusable item at the end of the list. See b/17387103.
191         if (getSelectedPosition() >= mItemsSize) {
192             setSelectedPosition(mItemsSize - 1);
193         }
194     }
195 
updateRatingLevels()196     private void updateRatingLevels() {
197         @ContentRatingLevel int ratingLevel = mParentalControlSettings.getContentRatingLevel();
198         for (RatingLevelItem ratingLevelItem : mRatingLevelItems) {
199             ratingLevelItem.setChecked(ratingLevel == ratingLevelItem.mRatingLevel);
200         }
201     }
202 
updateDependentRatingItems( ContentRatingSystem.Order order, int selectedRatingOrderIndex, String contentRatingSystemId, boolean isChecked)203     private void updateDependentRatingItems(
204             ContentRatingSystem.Order order,
205             int selectedRatingOrderIndex,
206             String contentRatingSystemId,
207             boolean isChecked) {
208         List<RatingItem> ratingItems = mContentRatingSystemItemMap.get(contentRatingSystemId);
209         if (ratingItems != null) {
210             for (RatingItem item : ratingItems) {
211                 int ratingOrderIndex = item.getRatingOrderIndex(order);
212                 if (ratingOrderIndex != -1
213                         && ((ratingOrderIndex > selectedRatingOrderIndex && isChecked)
214                                 || (ratingOrderIndex < selectedRatingOrderIndex && !isChecked))) {
215                     item.setRatingBlocked(isChecked);
216                 }
217             }
218         }
219     }
220 
221     private class RatingLevelItem extends RadioButtonItem {
222         private final int mRatingLevel;
223 
RatingLevelItem(int ratingLevel)224         private RatingLevelItem(int ratingLevel) {
225             super(
226                     getString(sLevelResourceIdMap.get(ratingLevel)),
227                     (sDescriptionResourceIdMap.indexOfKey(ratingLevel) >= 0)
228                             ? getString(sDescriptionResourceIdMap.get(ratingLevel))
229                             : null);
230             mRatingLevel = ratingLevel;
231         }
232 
233         @Override
onSelected()234         protected void onSelected() {
235             super.onSelected();
236             mParentalControlSettings.setContentRatingLevel(
237                     getMainActivity().getContentRatingsManager(), mRatingLevel);
238             if (mBlockUnratedItem != null
239                     && Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
240                 // set checked if UNRATED is blocked, and set unchecked otherwise.
241                 mBlockUnratedItem.setChecked(
242                         mParentalControlSettings.isRatingBlocked(
243                                 ImmutableList.of(TvContentRating.UNRATED)));
244             }
245             notifyItemsChanged(mRatingLevelItems.size());
246         }
247     }
248 
249     private class RatingItem extends CheckBoxItem {
250         protected final ContentRatingSystem mContentRatingSystem;
251         protected final Rating mRating;
252         private final Drawable mIcon;
253         private CompoundButton mCompoundButton;
254         private final List<ContentRatingSystem.Order> mOrders = new ArrayList<>();
255         private final List<Integer> mOrderIndexes = new ArrayList<>();
256 
RatingItem(ContentRatingSystem contentRatingSystem, Rating rating)257         private RatingItem(ContentRatingSystem contentRatingSystem, Rating rating) {
258             super(rating.getTitle(), rating.getDescription());
259             mContentRatingSystem = contentRatingSystem;
260             mRating = rating;
261             mIcon = rating.getIcon();
262             for (ContentRatingSystem.Order order : mContentRatingSystem.getOrders()) {
263                 int orderIndex = order.getRatingIndex(mRating);
264                 if (orderIndex != -1) {
265                     mOrders.add(order);
266                     mOrderIndexes.add(orderIndex);
267                 }
268             }
269         }
270 
271         @Override
onBind(View view)272         protected void onBind(View view) {
273             super.onBind(view);
274 
275             mCompoundButton = (CompoundButton) view.findViewById(getCompoundButtonId());
276             mCompoundButton.setVisibility(View.VISIBLE);
277 
278             ImageView imageView = (ImageView) view.findViewById(R.id.icon);
279             if (mIcon != null) {
280                 imageView.setVisibility(View.VISIBLE);
281                 imageView.setImageDrawable(mIcon);
282             } else {
283                 imageView.setVisibility(View.GONE);
284             }
285         }
286 
287         @Override
onUnbind()288         protected void onUnbind() {
289             super.onUnbind();
290             mCompoundButton = null;
291         }
292 
293         @Override
onUpdate()294         protected void onUpdate() {
295             super.onUpdate();
296             mCompoundButton.setButtonDrawable(getButtonDrawable());
297             setChecked(mParentalControlSettings.isRatingBlocked(mContentRatingSystem, mRating));
298         }
299 
300         @Override
onSelected()301         protected void onSelected() {
302             super.onSelected();
303             if (mParentalControlSettings.setRatingBlocked(
304                     mContentRatingSystem, mRating, isChecked())) {
305                 updateRatingLevels();
306             }
307             // Automatically check/uncheck dependent ratings.
308             for (int i = 0; i < mOrders.size(); i++) {
309                 updateDependentRatingItems(
310                         mOrders.get(i),
311                         mOrderIndexes.get(i),
312                         mContentRatingSystem.getId(),
313                         isChecked());
314             }
315         }
316 
317         @Override
getResourceId()318         protected int getResourceId() {
319             return R.layout.option_item_rating;
320         }
321 
getButtonDrawable()322         protected int getButtonDrawable() {
323             return R.drawable.btn_lock_material_anim;
324         }
325 
getRatingOrderIndex(ContentRatingSystem.Order order)326         private int getRatingOrderIndex(ContentRatingSystem.Order order) {
327             int orderIndex = mOrders.indexOf(order);
328             return orderIndex == -1 ? -1 : mOrderIndexes.get(orderIndex);
329         }
330 
setRatingBlocked(boolean isChecked)331         private void setRatingBlocked(boolean isChecked) {
332             if (isChecked() == isChecked) {
333                 return;
334             }
335             mParentalControlSettings.setRatingBlocked(mContentRatingSystem, mRating, isChecked);
336             notifyUpdated();
337         }
338     }
339 
340     private class RatingWithSubItem extends RatingItem {
RatingWithSubItem(ContentRatingSystem contentRatingSystem, Rating rating)341         private RatingWithSubItem(ContentRatingSystem contentRatingSystem, Rating rating) {
342             super(contentRatingSystem, rating);
343         }
344 
345         @Override
onSelected()346         protected void onSelected() {
347             getMainActivity()
348                     .getOverlayManager()
349                     .getSideFragmentManager()
350                     .show(SubRatingsFragment.create(mContentRatingSystem, mRating.getName()));
351         }
352 
353         @Override
getButtonDrawable()354         protected int getButtonDrawable() {
355             int blockedStatus =
356                     mParentalControlSettings.getBlockedStatus(mContentRatingSystem, mRating);
357             if (blockedStatus == ParentalControlSettings.RATING_BLOCKED) {
358                 return R.drawable.btn_lock_material;
359             } else if (blockedStatus == ParentalControlSettings.RATING_BLOCKED_PARTIAL) {
360                 return R.drawable.btn_partial_lock_material;
361             }
362             return R.drawable.btn_unlock_material;
363         }
364     }
365 
366     /** Opens a dialog showing the sources of the rating descriptions. */
367     public static class AttributionItem extends Item {
368         public static final String DIALOG_TAG = AttributionItem.class.getSimpleName();
369         public static final String TRACKER_LABEL = "Sources for content rating systems";
370         private final MainActivity mMainActivity;
371 
AttributionItem(MainActivity mainActivity)372         public AttributionItem(MainActivity mainActivity) {
373             mMainActivity = mainActivity;
374         }
375 
376         @Override
getResourceId()377         protected int getResourceId() {
378             return R.layout.option_item_attribution;
379         }
380 
381         @Override
onSelected()382         protected void onSelected() {
383             WebDialogFragment dialog =
384                     WebDialogFragment.newInstance(
385                             LicenseUtils.RATING_SOURCE_FILE,
386                             mMainActivity.getString(R.string.option_attribution),
387                             TRACKER_LABEL);
388             mMainActivity.getOverlayManager().showDialogFragment(DIALOG_TAG, dialog, false);
389         }
390     }
391 }
392