blob: 6b1f5504fbe651fd2cc35c417f681f94da66b200 [file] [log] [blame]
/*
* Copyright (C) 2021 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.permissioncontroller.permission.ui.handheld.dashboard;
import android.Manifest;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.permissioncontroller.R;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* A Preference for the permission usage graphic.
*/
public class PermissionUsageGraphicPreference extends Preference {
/** Permission group to count mapping. */
private @NonNull Map<String, Integer> mUsages = new HashMap<>();
/** Whether to show the "Other" category. */
private boolean mShowOtherCategory;
public PermissionUsageGraphicPreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public PermissionUsageGraphicPreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public PermissionUsageGraphicPreference(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public PermissionUsageGraphicPreference(@NonNull Context context) {
super(context);
init();
}
private void init() {
setLayoutResource(R.layout.permission_usage_graphic);
setSelectable(false);
}
/** Sets permission group usages: map of group name to usage count. */
public void setUsages(Map<String, Integer> usages) {
if (!Objects.equals(mUsages, usages)) {
mUsages = usages;
notifyChanged();
}
}
/** Sets whether to show the "Other" category. */
public void setShowOtherCategory(boolean showOtherCategory) {
if (mShowOtherCategory != showOtherCategory) {
mShowOtherCategory = showOtherCategory;
notifyChanged();
}
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
boolean isUsagesEmpty = isUsagesEmpty();
CompositeCircleView ccv =
(CompositeCircleView) holder.findViewById(R.id.composite_circle_view);
CompositeCircleViewLabeler ccvl = (CompositeCircleViewLabeler) holder.findViewById(
R.id.composite_circle_view_labeler);
// Set center text.
// TODO(b/176902658): Fix text appearance.
TextView centerLabel = new TextView(getContext());
centerLabel.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);
centerLabel.setText(getContext().getString(R.string.privdash_label_24h));
centerLabel.setTextAppearance(R.style.PrivacyDashboardGraphicLabel);
// Sample colors.
final int colorCamera = getContext().getColor(android.R.color.system_accent1_300);
final int colorMicrophone = getContext().getColor(android.R.color.system_accent1_100);
final int colorLocation = getContext().getColor(android.R.color.system_accent3_100);
final int colorOther = getContext().getColor(android.R.color.system_accent2_100);
// Create labels, counts, and colors.
TextView[] labels;
int[] counts;
int[] colors;
if (isUsagesEmpty) {
// Special case if usages are empty.
labels = new TextView[] { new TextView(getContext()) };
labels[0] = null;
counts = new int[] { 1 };
colors = new int[] { colorOther };
} else {
labels = new TextView[] {
new TextView(getContext()),
new TextView(getContext()),
new TextView(getContext()),
new TextView(getContext())
};
labels[0].setText(getContext().getString(R.string.privdash_label_camera));
labels[1].setText(getContext().getString(R.string.privdash_label_microphone));
labels[2].setText(getContext().getString(R.string.privdash_label_location));
labels[3].setText(getContext().getString(R.string.privdash_label_other));
counts = new int[] {
getUsageCount(Manifest.permission_group.CAMERA),
getUsageCount(Manifest.permission_group.MICROPHONE),
getUsageCount(Manifest.permission_group.LOCATION),
mShowOtherCategory
? getUsageCountExcluding(Manifest.permission_group.CAMERA,
Manifest.permission_group.MICROPHONE,
Manifest.permission_group.LOCATION) : 0
};
colors = new int[] {
colorCamera,
colorMicrophone,
colorLocation,
colorOther
};
}
// Set label styles.
for (int i = 0; i < labels.length; i++) {
if (labels[i] != null) {
labels[i].setTextAppearance(R.style.PrivacyDashboardGraphicLabel);
}
}
// Get circle-related dimensions.
TypedValue outValue = new TypedValue();
getContext().getResources().getValue(R.dimen.privhub_label_radius_scalar,
outValue, true);
float labelRadiusScalar = outValue.getFloat();
int circleStrokeWidth = (int) getContext().getResources().getDimension(
R.dimen.privhub_circle_stroke_width);
// Configure circle and labeler.
ccvl.configure(R.id.composite_circle_view, centerLabel, labels, labelRadiusScalar);
// Start at angle 300 (top right) to allow for small segments for cam, mic, and loc.
ccv.configure(300, counts, colors, circleStrokeWidth);
}
private int getUsageCount(String group) {
Integer count = mUsages.get(group);
if (count == null) {
return 0;
}
return count;
}
private int getUsageCountExcluding(String... excludeGroups) {
int count = 0;
List<String> exclude = Arrays.asList(excludeGroups);
for (Map.Entry<String, Integer> entry : mUsages.entrySet()) {
if (exclude.indexOf(entry.getKey()) >= 0) {
continue;
}
count += entry.getValue();
}
return count;
}
private boolean isUsagesEmpty() {
return getUsageCountExcluding() == 0;
}
}