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 data class SafetyCenterUiData(
33     val safetyCenterData: SafetyCenterData,
34     val resolvedIssues: Map<IssueId, ActionId> = emptyMap()
35 ) {
36     @RequiresApi(TIRAMISU)
getMatchingIssuenull37     fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? {
38         return safetyCenterData.issues.find {
39             SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey
40         }
41     }
42 
43     /** Returns the [SafetyCenterEntryGroup] corresponding to the provided ID */
44     @RequiresApi(UPSIDE_DOWN_CAKE)
getMatchingGroupnull45     fun getMatchingGroup(groupId: String): SafetyCenterEntryGroup? {
46         val entryOrGroups: List<SafetyCenterEntryOrGroup> = safetyCenterData.entriesOrGroups
47         val entryGroups = entryOrGroups.mapNotNull { it.entryGroup }
48         return entryGroups.find { it.id == groupId }
49     }
50 
51     /**
52      * Returns a list of [SafetyCenterIssue] corresponding to the provided ID. This will be
53      * displayed as warning cards on a subpage in Safety Center.
54      */
55     @RequiresApi(UPSIDE_DOWN_CAKE)
getMatchingIssuesnull56     fun getMatchingIssues(groupId: String): List<SafetyCenterIssue> =
57         selectMatchingIssuesForGroup(groupId, safetyCenterData.issues)
58 
59     /**
60      * Returns a list of dismissed [SafetyCenterIssue] corresponding to the provided ID. This will
61      * be displayed as dismissed warning cards on a subpage in Safety Center.
62      */
63     @RequiresApi(UPSIDE_DOWN_CAKE)
64     fun getMatchingDismissedIssues(groupId: String): List<SafetyCenterIssue> =
65         selectMatchingIssuesForGroup(groupId, safetyCenterData.visibleDismissedIssues())
66 
67     @RequiresApi(UPSIDE_DOWN_CAKE)
68     private fun selectMatchingIssuesForGroup(
69         groupId: String,
70         issues: List<SafetyCenterIssue>
71     ): List<SafetyCenterIssue> {
72         val issuesToGroups = safetyCenterData.extras.getBundle(ISSUES_TO_GROUPS_BUNDLE_KEY)
73         return issues.filter {
74             val mappingExists = issuesToGroups?.containsKey(it.id) ?: false
75             val matchesInMapping =
76                 issuesToGroups?.getStringArrayList(it.id)?.contains(groupId) ?: false
77             val matchesByDefault = it.groupId == groupId
78 
79             if (mappingExists) matchesInMapping else matchesByDefault
80         }
81     }
82 
83     /** Returns the [SafetyCenterData.getDismissedIssues] that are meant to be visible in the UI. */
84     @RequiresApi(UPSIDE_DOWN_CAKE)
SafetyCenterDatanull85     fun SafetyCenterData.visibleDismissedIssues() =
86         dismissedIssues.filter { it.severityLevel > ISSUE_SEVERITY_LEVEL_OK }
87 }
88