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