blob: 320ddcfcfd7f1e1cbfd7f4ed7d710c9bbf371201 [file] [log] [blame]
/*
* Copyright (C) 2022 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.safetycenter.ui;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.INVALID_TASK_ID;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
import android.safetycenter.SafetyCenterEntry;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
/** A preference that displays a visual representation of a {@link SafetyCenterEntry}. */
@RequiresApi(TIRAMISU)
public final class SafetyEntryPreference extends Preference implements ComparablePreference {
private static final String TAG = SafetyEntryPreference.class.getSimpleName();
private final PositionInCardList mPosition;
private final SafetyCenterEntry mEntry;
private final SafetyCenterViewModel mViewModel;
private final CharSequence mGroupId;
public SafetyEntryPreference(
Context context,
int taskId,
SafetyCenterEntry entry,
CharSequence groupId,
PositionInCardList position,
SafetyCenterViewModel viewModel) {
super(context);
mEntry = entry;
mPosition = position;
mViewModel = viewModel;
mGroupId = groupId;
setLayoutResource(R.layout.preference_entry);
setTitle(entry.getTitle());
setSummary(entry.getSummary());
setIcon(SeverityIconPicker.selectIconResId(
mEntry.getSeverityLevel(), mEntry.getSeverityUnspecifiedIconType()));
PendingIntent pendingIntent = entry.getPendingIntent();
if (pendingIntent != null) {
setOnPreferenceClickListener(
unused -> {
try {
ActivityOptions options = ActivityOptions.makeBasic();
if (taskId != INVALID_TASK_ID) {
options.setLaunchTaskId(taskId);
}
entry.getPendingIntent()
.send(context, 0, null, null, null, null, options.toBundle());
mViewModel
.getInteractionLogger()
.recordForEntry(Action.ENTRY_CLICKED, mEntry);
} catch (Exception ex) {
Log.e(
TAG,
String.format(
"Failed to execute pending intent for entry: %s",
entry),
ex);
}
return true;
});
}
SafetyCenterEntry.IconAction iconAction = entry.getIconAction();
if (iconAction != null) {
setWidgetLayoutResource(
iconAction.getType() == SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR
? R.layout.preference_entry_icon_action_gear_widget
: R.layout.preference_entry_icon_action_info_widget);
}
}
@Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
holder.itemView.setBackgroundResource(mPosition.getBackgroundDrawableResId());
final int topMargin = mPosition.getTopMargin(holder.itemView.getContext());
final ViewGroup.MarginLayoutParams params =
(ViewGroup.MarginLayoutParams) holder.itemView.getLayoutParams();
if (params.topMargin != topMargin) {
params.topMargin = topMargin;
holder.itemView.setLayoutParams(params);
}
boolean hideIcon =
mEntry.getSeverityLevel() == SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED
&& mEntry.getSeverityUnspecifiedIconType()
== SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON;
holder.findViewById(R.id.icon_frame).setVisibility(hideIcon ? View.GONE : View.VISIBLE);
holder.findViewById(R.id.empty_space).setVisibility(hideIcon ? View.VISIBLE : View.GONE);
enableOrDisableEntry(holder);
SafetyCenterEntry.IconAction iconAction = mEntry.getIconAction();
if (iconAction != null) {
holder.findViewById(R.id.icon_action_button)
.setOnClickListener(
view -> {
sendIconActionIntent(iconAction);
mViewModel
.getInteractionLogger()
.recordForEntry(Action.ENTRY_ICON_ACTION_CLICKED, mEntry);
});
holder.itemView.setPaddingRelative(
holder.itemView.getPaddingStart(),
holder.itemView.getPaddingTop(),
/* end= */ 0,
holder.itemView.getPaddingBottom());
} else {
holder.itemView.setPaddingRelative(
holder.itemView.getPaddingStart(),
holder.itemView.getPaddingTop(),
/* end= */ getContext()
.getResources()
.getDimensionPixelSize(R.dimen.safety_center_entry_padding_end),
holder.itemView.getPaddingBottom());
}
}
private void sendIconActionIntent(SafetyCenterEntry.IconAction iconAction) {
try {
iconAction.getPendingIntent().send();
} catch (Exception ex) {
Log.e(
TAG,
String.format("Failed to execute icon action intent for entry: %s", mEntry),
ex);
}
}
/** We are doing this because we need some entries to look disabled but still be clickable. */
private void enableOrDisableEntry(@NonNull PreferenceViewHolder holder) {
holder.itemView.setEnabled(mEntry.getPendingIntent() != null);
if (mEntry.isEnabled()) {
holder.findViewById(android.R.id.title).setAlpha(1F);
holder.findViewById(android.R.id.summary).setAlpha(1F);
} else {
holder.findViewById(android.R.id.title).setAlpha(0.4F);
holder.findViewById(android.R.id.summary).setAlpha(0.4F);
}
}
public CharSequence getGroupId() {
return mGroupId;
}
@Override
public boolean isSameItem(@NonNull Preference other) {
return other instanceof SafetyEntryPreference
&& TextUtils.equals(mEntry.getId(), ((SafetyEntryPreference) other).mEntry.getId())
&& TextUtils.equals(mGroupId, ((SafetyEntryPreference) other).mGroupId);
}
@Override
public boolean hasSameContents(@NonNull Preference other) {
if (other instanceof SafetyEntryPreference) {
SafetyEntryPreference o = (SafetyEntryPreference) other;
return mEntry.equals(o.mEntry) && mPosition == o.mPosition;
}
return false;
}
}