/* * 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 android.content.Context import android.os.Build import android.os.UserHandle import android.os.UserManager import android.safetycenter.SafetyCenterEntry import android.safetycenter.SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR import android.text.TextUtils import android.util.Log import android.widget.ImageView import android.widget.TextView import androidx.annotation.RequiresApi import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.R import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PERSONAL_PROFILE_SUFFIX import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel import com.android.permissioncontroller.safetycenter.ui.view.SafetyEntryCommonViewsManager.Companion.changeEnabledState import com.android.safetycenter.internaldata.SafetyCenterEntryId import com.android.safetycenter.internaldata.SafetyCenterIds import com.android.settingslib.widget.TwoTargetPreference /** * A preference that displays a visual representation of a {@link SafetyCenterEntry} on the Safety * Center subpage. */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) class SafetySubpageEntryPreference( context: Context, private val launchTaskId: Int?, private val entry: SafetyCenterEntry, private val viewModel: SafetyCenterViewModel ) : TwoTargetPreference(context), ComparablePreference { init { setupIconActionButton() setupClickListener() setTitle(entry.title) setSummary(entry.summary) setSelectable(true) setupPreferenceKey() } private fun setupIconActionButton() { if (entry.iconAction != null) { setIconSize(ICON_SIZE_MEDIUM) setWidgetLayoutResource( if (entry.iconAction!!.type == ICON_ACTION_TYPE_GEAR) { R.layout.preference_entry_icon_action_gear_widget } else { R.layout.preference_entry_icon_action_info_widget } ) } } private fun setupClickListener() { val pendingIntent = entry.pendingIntent if (pendingIntent != null) { setOnPreferenceClickListener { try { PendingIntentSender.send(pendingIntent, launchTaskId) viewModel.interactionLogger.recordForEntry(Action.ENTRY_CLICKED, entry) true } catch (ex: Exception) { Log.e(TAG, "Failed to execute pending intent for $entry", ex) false } } setEnabled(true) } else { Log.w(TAG, "Pending intent is null for $entry") setEnabled(false) } } private fun setupPreferenceKey() { val entryId: SafetyCenterEntryId = SafetyCenterIds.entryIdFromString(entry.id) val userContext = context.createContextAsUser(UserHandle.of(entryId.userId), /* flags= */ 0) val userUserManager = userContext.getSystemService(UserManager::class.java) ?: return if (userUserManager.isManagedProfile) { setKey("${entryId.safetySourceId}_$WORK_PROFILE_SUFFIX") } else if (isPrivateProfileSupported() && userUserManager.isPrivateProfile) { setKey("${entryId.safetySourceId}_$PRIVATE_PROFILE_SUFFIX") } else { setKey("${entryId.safetySourceId}_$PERSONAL_PROFILE_SUFFIX") } } private fun isPrivateProfileSupported(): Boolean { return SdkLevel.isAtLeastV() && com.android.permission.flags.Flags.privateProfileSupported() && android.os.Flags.allowPrivateProfile() } override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) val iconAction = entry.iconAction if (iconAction == null) { Log.w(TAG, "Icon action is null for $entry") } else { val iconActionButton = holder.findViewById(R.id.icon_action_button) as? ImageView? iconActionButton?.setOnClickListener { try { PendingIntentSender.send(iconAction.pendingIntent, launchTaskId) viewModel.interactionLogger.recordForEntry( Action.ENTRY_ICON_ACTION_CLICKED, entry ) } catch (ex: Exception) { Log.e(TAG, "Failed to execute icon action intent for $entry", ex) } } } val titleView = holder.findViewById(android.R.id.title) as? TextView? val summaryView = holder.findViewById(android.R.id.summary) as? TextView? changeEnabledState(context, entry.isEnabled, isEnabled(), titleView, summaryView) } override fun shouldHideSecondTarget(): Boolean = entry.iconAction == null override fun isSameItem(preference: Preference): Boolean = preference is SafetySubpageEntryPreference && TextUtils.equals(entry.id, preference.entry.id) override fun hasSameContents(preference: Preference): Boolean = preference is SafetySubpageEntryPreference && entry == preference.entry companion object { private val TAG: String = SafetySubpageEntryPreference::class.java.simpleName } }