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