blob: d1ae442304c8ac1d6ab7bd94721b007be50cccc2 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.ui.sidepanel.parentalcontrols;
import android.graphics.drawable.Drawable;
import android.media.tv.TvContentRating;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.SparseIntArray;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageView;
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.common.experiments.Experiments;
import com.android.tv.dialog.WebDialogFragment;
import com.android.tv.license.LicenseUtils;
import com.android.tv.parental.ContentRatingSystem;
import com.android.tv.parental.ContentRatingSystem.Rating;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.ui.sidepanel.CheckBoxItem;
import com.android.tv.ui.sidepanel.DividerItem;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.RadioButtonItem;
import com.android.tv.ui.sidepanel.SideFragment;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvSettings.ContentRatingLevel;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class RatingsFragment extends SideFragment {
private static final SparseIntArray sLevelResourceIdMap;
private static final SparseIntArray sDescriptionResourceIdMap;
private static final String TRACKER_LABEL = "Ratings";
private int mItemsSize;
static {
sLevelResourceIdMap = new SparseIntArray(5);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_NONE, R.string.option_rating_none);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high);
sLevelResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium);
sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low);
sLevelResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom);
sDescriptionResourceIdMap = new SparseIntArray(sLevelResourceIdMap.size());
sDescriptionResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high_description);
sDescriptionResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium_description);
sDescriptionResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low_description);
sDescriptionResourceIdMap.put(
TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom_description);
}
private final List<RatingLevelItem> mRatingLevelItems = new ArrayList<>();
// A map from the rating system ID string to RatingItem objects.
private final Map<String, List<RatingItem>> mContentRatingSystemItemMap = new ArrayMap<>();
private CheckBoxItem mBlockUnratedItem;
private ParentalControlSettings mParentalControlSettings;
public static String getDescription(MainActivity tvActivity) {
@ContentRatingLevel
int currentLevel = tvActivity.getParentalControlSettings().getContentRatingLevel();
if (sLevelResourceIdMap.indexOfKey(currentLevel) >= 0) {
return tvActivity.getString(sLevelResourceIdMap.get(currentLevel));
}
return null;
}
@Override
protected String getTitle() {
return getString(R.string.option_ratings);
}
@Override
public String getTrackerLabel() {
return TRACKER_LABEL;
}
@Override
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
if (mBlockUnratedItem != null
&& Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
items.add(mBlockUnratedItem);
items.add(new DividerItem());
}
mRatingLevelItems.clear();
for (int i = 0; i < sLevelResourceIdMap.size(); ++i) {
mRatingLevelItems.add(new RatingLevelItem(sLevelResourceIdMap.keyAt(i)));
}
updateRatingLevels();
items.addAll(mRatingLevelItems);
mContentRatingSystemItemMap.clear();
List<ContentRatingSystem> contentRatingSystems =
getMainActivity().getContentRatingsManager().getContentRatingSystems();
Collections.sort(contentRatingSystems, ContentRatingSystem.DISPLAY_NAME_COMPARATOR);
for (ContentRatingSystem s : contentRatingSystems) {
if (mParentalControlSettings.isContentRatingSystemEnabled(s)) {
List<RatingItem> ratingItems = new ArrayList<>();
boolean hasSubRating = false;
items.add(new DividerItem(s.getDisplayName()));
for (Rating rating : s.getRatings()) {
RatingItem item =
rating.getSubRatings().isEmpty()
? new RatingItem(s, rating)
: new RatingWithSubItem(s, rating);
items.add(item);
if (rating.getSubRatings().isEmpty()) {
ratingItems.add(item);
} else {
hasSubRating = true;
}
}
// Only include rating systems that don't contain any sub ratings in the map for
// simplicity.
if (!hasSubRating) {
mContentRatingSystemItemMap.put(s.getId(), ratingItems);
}
}
}
if (LicenseUtils.hasRatingAttribution(getMainActivity().getAssets())) {
// Display the attribution if our content rating system is selected.
items.add(new DividerItem());
items.add(new AttributionItem(getMainActivity()));
}
mItemsSize = items.size();
return items;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mParentalControlSettings = getMainActivity().getParentalControlSettings();
mParentalControlSettings.loadRatings();
if (Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
mBlockUnratedItem =
new CheckBoxItem(
getResources().getString(R.string.option_block_unrated_programs)) {
@Override
protected void onUpdate() {
super.onUpdate();
setChecked(
mParentalControlSettings.isRatingBlocked(
ImmutableList.of(TvContentRating.UNRATED)));
}
@Override
protected void onSelected() {
super.onSelected();
if (mParentalControlSettings.setUnratedBlocked(isChecked())) {
updateRatingLevels();
}
}
};
}
}
@Override
public void onResume() {
super.onResume();
// Although we set the attribution item at the end of the item list non-focusable, we do get
// its position when the fragment is resumed. This ensures that we do not select the
// non-focusable item at the end of the list. See b/17387103.
if (getSelectedPosition() >= mItemsSize) {
setSelectedPosition(mItemsSize - 1);
}
}
private void updateRatingLevels() {
@ContentRatingLevel int ratingLevel = mParentalControlSettings.getContentRatingLevel();
for (RatingLevelItem ratingLevelItem : mRatingLevelItems) {
ratingLevelItem.setChecked(ratingLevel == ratingLevelItem.mRatingLevel);
}
}
private void updateDependentRatingItems(
ContentRatingSystem.Order order,
int selectedRatingOrderIndex,
String contentRatingSystemId,
boolean isChecked) {
List<RatingItem> ratingItems = mContentRatingSystemItemMap.get(contentRatingSystemId);
if (ratingItems != null) {
for (RatingItem item : ratingItems) {
int ratingOrderIndex = item.getRatingOrderIndex(order);
if (ratingOrderIndex != -1
&& ((ratingOrderIndex > selectedRatingOrderIndex && isChecked)
|| (ratingOrderIndex < selectedRatingOrderIndex && !isChecked))) {
item.setRatingBlocked(isChecked);
}
}
}
}
private class RatingLevelItem extends RadioButtonItem {
private final int mRatingLevel;
private RatingLevelItem(int ratingLevel) {
super(
getString(sLevelResourceIdMap.get(ratingLevel)),
(sDescriptionResourceIdMap.indexOfKey(ratingLevel) >= 0)
? getString(sDescriptionResourceIdMap.get(ratingLevel))
: null);
mRatingLevel = ratingLevel;
}
@Override
protected void onSelected() {
super.onSelected();
mParentalControlSettings.setContentRatingLevel(
getMainActivity().getContentRatingsManager(), mRatingLevel);
if (mBlockUnratedItem != null
&& Boolean.TRUE.equals(Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get())) {
// set checked if UNRATED is blocked, and set unchecked otherwise.
mBlockUnratedItem.setChecked(
mParentalControlSettings.isRatingBlocked(
ImmutableList.of(TvContentRating.UNRATED)));
}
notifyItemsChanged(mRatingLevelItems.size());
}
}
private class RatingItem extends CheckBoxItem {
protected final ContentRatingSystem mContentRatingSystem;
protected final Rating mRating;
private final Drawable mIcon;
private CompoundButton mCompoundButton;
private final List<ContentRatingSystem.Order> mOrders = new ArrayList<>();
private final List<Integer> mOrderIndexes = new ArrayList<>();
private RatingItem(ContentRatingSystem contentRatingSystem, Rating rating) {
super(rating.getTitle(), rating.getDescription());
mContentRatingSystem = contentRatingSystem;
mRating = rating;
mIcon = rating.getIcon();
for (ContentRatingSystem.Order order : mContentRatingSystem.getOrders()) {
int orderIndex = order.getRatingIndex(mRating);
if (orderIndex != -1) {
mOrders.add(order);
mOrderIndexes.add(orderIndex);
}
}
}
@Override
protected void onBind(View view) {
super.onBind(view);
mCompoundButton = (CompoundButton) view.findViewById(getCompoundButtonId());
mCompoundButton.setVisibility(View.VISIBLE);
ImageView imageView = (ImageView) view.findViewById(R.id.icon);
if (mIcon != null) {
imageView.setVisibility(View.VISIBLE);
imageView.setImageDrawable(mIcon);
} else {
imageView.setVisibility(View.GONE);
}
}
@Override
protected void onUnbind() {
super.onUnbind();
mCompoundButton = null;
}
@Override
protected void onUpdate() {
super.onUpdate();
mCompoundButton.setButtonDrawable(getButtonDrawable());
setChecked(mParentalControlSettings.isRatingBlocked(mContentRatingSystem, mRating));
}
@Override
protected void onSelected() {
super.onSelected();
if (mParentalControlSettings.setRatingBlocked(
mContentRatingSystem, mRating, isChecked())) {
updateRatingLevels();
}
// Automatically check/uncheck dependent ratings.
for (int i = 0; i < mOrders.size(); i++) {
updateDependentRatingItems(
mOrders.get(i),
mOrderIndexes.get(i),
mContentRatingSystem.getId(),
isChecked());
}
}
@Override
protected int getResourceId() {
return R.layout.option_item_rating;
}
protected int getButtonDrawable() {
return R.drawable.btn_lock_material_anim;
}
private int getRatingOrderIndex(ContentRatingSystem.Order order) {
int orderIndex = mOrders.indexOf(order);
return orderIndex == -1 ? -1 : mOrderIndexes.get(orderIndex);
}
private void setRatingBlocked(boolean isChecked) {
if (isChecked() == isChecked) {
return;
}
mParentalControlSettings.setRatingBlocked(mContentRatingSystem, mRating, isChecked);
notifyUpdated();
}
}
private class RatingWithSubItem extends RatingItem {
private RatingWithSubItem(ContentRatingSystem contentRatingSystem, Rating rating) {
super(contentRatingSystem, rating);
}
@Override
protected void onSelected() {
getMainActivity()
.getOverlayManager()
.getSideFragmentManager()
.show(SubRatingsFragment.create(mContentRatingSystem, mRating.getName()));
}
@Override
protected int getButtonDrawable() {
int blockedStatus =
mParentalControlSettings.getBlockedStatus(mContentRatingSystem, mRating);
if (blockedStatus == ParentalControlSettings.RATING_BLOCKED) {
return R.drawable.btn_lock_material;
} else if (blockedStatus == ParentalControlSettings.RATING_BLOCKED_PARTIAL) {
return R.drawable.btn_partial_lock_material;
}
return R.drawable.btn_unlock_material;
}
}
/** Opens a dialog showing the sources of the rating descriptions. */
public static class AttributionItem extends Item {
public static final String DIALOG_TAG = AttributionItem.class.getSimpleName();
public static final String TRACKER_LABEL = "Sources for content rating systems";
private final MainActivity mMainActivity;
public AttributionItem(MainActivity mainActivity) {
mMainActivity = mainActivity;
}
@Override
protected int getResourceId() {
return R.layout.option_item_attribution;
}
@Override
protected void onSelected() {
WebDialogFragment dialog =
WebDialogFragment.newInstance(
LicenseUtils.RATING_SOURCE_FILE,
mMainActivity.getString(R.string.option_attribution),
TRACKER_LABEL);
mMainActivity.getOverlayManager().showDialogFragment(DIALOG_TAG, dialog, false);
}
}
}