1 /* 2 * Copyright (C) 2023 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.permissioncontroller.safetycenter.ui.model 18 19 import android.os.Build.VERSION_CODES.TIRAMISU 20 import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE 21 import android.safetycenter.SafetyCenterData 22 import android.safetycenter.SafetyCenterEntryGroup 23 import android.safetycenter.SafetyCenterEntryOrGroup 24 import android.safetycenter.SafetyCenterIssue 25 import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK 26 import androidx.annotation.RequiresApi 27 import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY 28 import com.android.safetycenter.internaldata.SafetyCenterIds 29 import com.android.safetycenter.internaldata.SafetyCenterIssueKey 30 31 /** UI model representation of Safety Center Data */ 32 @RequiresApi(TIRAMISU) 33 data class SafetyCenterUiData( 34 val safetyCenterData: SafetyCenterData, 35 private val taskId: Int, 36 private val sameTaskSourceIds: List<String>, 37 val resolvedIssues: Map<IssueId, ActionId> = emptyMap(), 38 ) { 39 40 val issueUiDatas: List<IssueUiData> by <lambda>null41 lazy(LazyThreadSafetyMode.NONE) { 42 safetyCenterData.issues.map { toIssueUiData(it, isDismissed = false) } 43 } 44 getMatchingIssuenull45 fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? { 46 return safetyCenterData.issues.find { 47 SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey 48 } 49 } 50 51 /** Returns the [SafetyCenterEntryGroup] corresponding to the provided ID */ 52 @RequiresApi(UPSIDE_DOWN_CAKE) getMatchingGroupnull53 fun getMatchingGroup(groupId: String): SafetyCenterEntryGroup? { 54 val entryOrGroups: List<SafetyCenterEntryOrGroup> = safetyCenterData.entriesOrGroups 55 val entryGroups = entryOrGroups.mapNotNull { it.entryGroup } 56 return entryGroups.find { it.id == groupId } 57 } 58 59 /** 60 * Returns a list of [SafetyCenterIssue] corresponding to the provided ID. This will be 61 * displayed as warning cards on a subpage in Safety Center. 62 */ 63 @RequiresApi(UPSIDE_DOWN_CAKE) getMatchingIssuesnull64 fun getMatchingIssues(groupId: String): List<SafetyCenterIssue> = 65 selectMatchingIssuesForGroup(groupId, safetyCenterData.issues) 66 67 /** 68 * Returns a list of dismissed [SafetyCenterIssue] corresponding to the provided ID. This will 69 * be displayed as dismissed warning cards on a subpage in Safety Center. 70 */ 71 @RequiresApi(UPSIDE_DOWN_CAKE) 72 fun getMatchingDismissedIssues(groupId: String): List<SafetyCenterIssue> = 73 selectMatchingIssuesForGroup(groupId, safetyCenterData.visibleDismissedIssues()) 74 75 @RequiresApi(UPSIDE_DOWN_CAKE) 76 private fun selectMatchingIssuesForGroup( 77 groupId: String, 78 issues: List<SafetyCenterIssue>, 79 ): List<SafetyCenterIssue> { 80 val issuesToGroups = safetyCenterData.extras.getBundle(ISSUES_TO_GROUPS_BUNDLE_KEY) 81 return issues.filter { 82 val mappingExists = issuesToGroups?.containsKey(it.id) ?: false 83 val matchesInMapping = 84 issuesToGroups?.getStringArrayList(it.id)?.contains(groupId) ?: false 85 val matchesByDefault = it.groupId == groupId 86 87 if (mappingExists) matchesInMapping else matchesByDefault 88 } 89 } 90 91 /** Returns the [SafetyCenterData.getDismissedIssues] that are meant to be visible in the UI. */ 92 @RequiresApi(UPSIDE_DOWN_CAKE) SafetyCenterDatanull93 private fun SafetyCenterData.visibleDismissedIssues() = 94 dismissedIssues.filter { it.severityLevel > ISSUE_SEVERITY_LEVEL_OK } 95 96 /** Converts a [SafetyCenterIssue] into [IssueUiData]. */ toIssueUiDatanull97 private fun toIssueUiData(issue: SafetyCenterIssue, isDismissed: Boolean) = 98 IssueUiData(issue, isDismissed, resolvedIssues[issue.id], getLaunchTaskIdForIssue(issue)) 99 100 private fun getLaunchTaskIdForIssue(issue: SafetyCenterIssue): Int? { 101 val sourceId: String = 102 SafetyCenterIds.issueIdFromString(issue.id) 103 .getSafetyCenterIssueKey() 104 .getSafetySourceId() 105 return if (sameTaskSourceIds.contains(sourceId)) taskId else null 106 } 107 } 108