summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Dewey <deweytyl@google.com> 2025-02-20 01:50:31 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-20 01:50:31 -0800
commitf4c9bdb9118400a98c95f119a5bc8f896e489c60 (patch)
tree1129a972091ed46854dd1b2319b991f3165b4776
parent9c528bffc307b8b199a46df7f2763f9527f1e93e (diff)
parentc80a38af51c9aaaee9b3bb675c0b2c0dc8fcf649 (diff)
Merge "Add initial data layer for issues." into main
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt27
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt41
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt24
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt212
5 files changed, 226 insertions, 91 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
index b5a66da06..d29b0aa3e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
@@ -61,7 +61,6 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() {
} else {
super.onCreateAdapter(preferenceScreen)
}
-
/* By default, the PreferenceGroupAdapter does setHasStableIds(true). Since each Preference
* is internally allocated with an auto-incremented ID, it does not allow us to gracefully
* update only changed preferences based on SafetyPreferenceComparisonCallback. In order to
@@ -77,10 +76,15 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() {
.split(",")
safetyCenterSessionId = requireArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID)
+ val activity = requireActivity()
safetyCenterViewModel =
ViewModelProvider(
- requireActivity(),
- LiveSafetyCenterViewModelFactory(requireActivity().getApplication()),
+ activity,
+ LiveSafetyCenterViewModelFactory(
+ activity.application,
+ activity.taskId,
+ sameTaskSourceIds,
+ ),
)
.get(SafetyCenterViewModel::class.java)
safetyCenterViewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? ->
@@ -91,8 +95,7 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() {
displayErrorDetails(errorDetails)
}
- val safetyCenterIntent: ParsedSafetyCenterIntent =
- requireActivity().intent.toSafetyCenterIntent()
+ val safetyCenterIntent: ParsedSafetyCenterIntent = activity.intent.toSafetyCenterIntent()
val isQsFragment =
getArguments()?.getBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, false) ?: false
collapsableIssuesCardHelper =
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt
new file mode 100644
index 000000000..e260bb917
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.model
+
+import android.safetycenter.SafetyCenterIssue
+
+/** UI model representation of [SafetyCenterIssue] */
+data class IssueUiData(
+ val issue: SafetyCenterIssue,
+ val isDismissed: Boolean,
+ val resolvedIssueActionId: String? = null,
+ val launchTaskId: Int? = null,
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
index 4ddcf1c3d..0b976f49d 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
@@ -43,11 +43,16 @@ import com.android.safetycenter.internaldata.SafetyCenterIds
/* A SafetyCenterViewModel that talks to the real backing service for Safety Center. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
-class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
+class LiveSafetyCenterViewModel(
+ app: Application,
+ private val taskId: Int,
+ private val sameTaskSourceIds: List<String>,
+) : SafetyCenterViewModel(app) {
private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName
override val statusUiLiveData: LiveData<StatusUiData>
get() = safetyCenterUiLiveData.map { StatusUiData(it.safetyCenterData) }
+
override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData
override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData
@@ -65,7 +70,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
private val safetyCenterManager = app.getSystemService(SafetyCenterManager::class.java)!!
override fun getCurrentSafetyCenterDataAsUiData(): SafetyCenterUiData =
- SafetyCenterUiData(safetyCenterManager.safetyCenterData)
+ uiData(safetyCenterManager.safetyCenterData)
override fun dismissIssue(issue: SafetyCenterIssue) {
safetyCenterManager.dismissSafetyCenterIssue(issue.id)
@@ -74,7 +79,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
override fun executeIssueAction(
issue: SafetyCenterIssue,
action: SafetyCenterIssue.Action,
- launchTaskId: Int?
+ launchTaskId: Int?,
) {
val issueId =
if (launchTaskId != null) {
@@ -107,9 +112,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
override fun navigateToSafetyCenter(context: Context, navigationSource: NavigationSource?) {
val intent = Intent(ACTION_SAFETY_CENTER)
- if (navigationSource != null) {
- navigationSource.addToIntent(intent)
- }
+ navigationSource?.addToIntent(intent)
context.startActivity(intent)
}
@@ -132,7 +135,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
} else {
safetyCenterManager.refreshSafetySources(
SafetyCenterManager.REFRESH_REASON_PAGE_OPEN,
- safetySourceIds
+ safetySourceIds,
)
}
}
@@ -174,7 +177,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
override fun onActive() {
safetyCenterManager.addOnSafetyCenterDataChangedListener(
getMainExecutor(app.applicationContext),
- this
+ this,
)
super.onActive()
}
@@ -209,7 +212,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
Log.d(
TAG,
"Received SafetyCenterData while issue resolution animations" +
- " occurring. Will update UI with new data soon."
+ " occurring. Will update UI with new data soon.",
)
return
}
@@ -254,7 +257,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
private fun isCurrentlyScanning(): Boolean = value?.safetyCenterData?.isScanning() ?: false
private fun sendNextData() {
- value = SafetyCenterUiData(safetyCenterDataQueue.removeFirst())
+ value = uiData(safetyCenterDataQueue.removeFirst())
}
private fun skipNextData() = safetyCenterDataQueue.removeFirst()
@@ -270,7 +273,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
// The current SafetyCenterData still contains the resolved SafetyCenterIssue objects.
// Send it with the resolved IDs so the UI can generate the correct preferences and
// trigger the right animations for issue resolution.
- value = SafetyCenterUiData(currentData, currentResolvedIssues)
+ value = uiData(currentData, currentResolvedIssues)
}
@MainThread
@@ -279,6 +282,11 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
maybeProcessDataToNextResolvedIssues()
}
}
+
+ private fun uiData(
+ safetyCenterData: SafetyCenterData,
+ resolvedIssues: Map<IssueId, ActionId> = emptyMap(),
+ ) = SafetyCenterUiData(safetyCenterData, taskId, sameTaskSourceIds, resolvedIssues)
}
/** Returns inflight issues pending resolution */
@@ -309,8 +317,15 @@ private val SafetyCenterData.allResolvableIssues: Sequence<SafetyCenterIssue>
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
-class LiveSafetyCenterViewModelFactory(private val app: Application) : ViewModelProvider.Factory {
+class LiveSafetyCenterViewModelFactory
+@JvmOverloads
+constructor(
+ private val app: Application,
+ private val taskId: Int = 0,
+ private val sameTaskSourceIds: List<String> = emptyList(),
+) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- @Suppress("UNCHECKED_CAST") return LiveSafetyCenterViewModel(app) as T
+ @Suppress("UNCHECKED_CAST")
+ return LiveSafetyCenterViewModel(app, taskId, sameTaskSourceIds) as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
index 69a315f08..d8aadae2f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt
@@ -29,11 +29,21 @@ import com.android.safetycenter.internaldata.SafetyCenterIds
import com.android.safetycenter.internaldata.SafetyCenterIssueKey
/** UI model representation of Safety Center Data */
+@RequiresApi(TIRAMISU)
data class SafetyCenterUiData(
val safetyCenterData: SafetyCenterData,
- val resolvedIssues: Map<IssueId, ActionId> = emptyMap()
+ private val taskId: Int,
+ private val sameTaskSourceIds: List<String>,
+ val resolvedIssues: Map<IssueId, ActionId> = emptyMap(),
) {
- @RequiresApi(TIRAMISU)
+
+ val issueUiDatas: List<IssueUiData> by
+ lazy(LazyThreadSafetyMode.NONE) {
+ safetyCenterData.issues.map {
+ IssueUiData(it, false, resolvedIssues[it.id], getLaunchTaskIdForIssue(it))
+ }
+ }
+
fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? {
return safetyCenterData.issues.find {
SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey
@@ -67,7 +77,7 @@ data class SafetyCenterUiData(
@RequiresApi(UPSIDE_DOWN_CAKE)
private fun selectMatchingIssuesForGroup(
groupId: String,
- issues: List<SafetyCenterIssue>
+ issues: List<SafetyCenterIssue>,
): List<SafetyCenterIssue> {
val issuesToGroups = safetyCenterData.extras.getBundle(ISSUES_TO_GROUPS_BUNDLE_KEY)
return issues.filter {
@@ -84,4 +94,12 @@ data class SafetyCenterUiData(
@RequiresApi(UPSIDE_DOWN_CAKE)
fun SafetyCenterData.visibleDismissedIssues() =
dismissedIssues.filter { it.severityLevel > ISSUE_SEVERITY_LEVEL_OK }
+
+ private fun getLaunchTaskIdForIssue(issue: SafetyCenterIssue): Int? {
+ val sourceId: String =
+ SafetyCenterIds.issueIdFromString(issue.id)
+ .getSafetyCenterIssueKey()
+ .getSafetySourceId()
+ return if (sameTaskSourceIds.contains(sourceId)) taskId else null
+ }
}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
index ca0392716..e53fabc90 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/SafetyCenterUiDataTest.kt
@@ -18,6 +18,7 @@ package com.android.permissioncontroller.tests.mocking.safetycenter.ui.model
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.Bundle
+import android.os.UserHandle
import android.safetycenter.SafetyCenterData
import android.safetycenter.SafetyCenterEntryGroup
import android.safetycenter.SafetyCenterEntryOrGroup
@@ -28,8 +29,14 @@ import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATIO
import android.safetycenter.SafetyCenterStatus
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
+import com.android.permissioncontroller.safetycenter.ui.model.ActionId
+import com.android.permissioncontroller.safetycenter.ui.model.IssueId
+import com.android.permissioncontroller.safetycenter.ui.model.IssueUiData
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY
+import com.android.safetycenter.internaldata.SafetyCenterIds
+import com.android.safetycenter.internaldata.SafetyCenterIssueId
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,42 +47,42 @@ class SafetyCenterUiDataTest {
@Test
fun getMatchingGroup_validMatchingGroup_returnsExpectedEntryGroup() {
- val matchingGroup = createSafetyCenterEntryGroup(MATCHING_GROUP_ID)
- val nonMatchingGroup = createSafetyCenterEntryGroup(NON_MATCHING_GROUP_ID)
+ val matchingGroup = entryGroup(MATCHING_GROUP_ID)
+ val nonMatchingGroup = entryGroup(NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(entryGroups = listOf(matchingGroup, nonMatchingGroup))
- val result = SafetyCenterUiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID)
assertThat(result).isEqualTo(matchingGroup)
}
@Test
fun getMatchingGroup_noMatchingGroup_returnsNull() {
- val nonMatchingGroup = createSafetyCenterEntryGroup(NON_MATCHING_GROUP_ID)
+ val nonMatchingGroup = entryGroup(NON_MATCHING_GROUP_ID)
val safetyCenterData = createSafetyCenterData(entryGroups = listOf(nonMatchingGroup))
- val result = SafetyCenterUiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID)
assertThat(result).isNull()
}
@Test
fun getMatchingIssues_defaultMatchingIssue_noExtras_returnsListOfIssues() {
- val defaultMatchingIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID)
- val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val defaultMatchingIssue = issue("id1", MATCHING_GROUP_ID)
+ val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(issues = listOf(defaultMatchingIssue, nonMatchingIssue))
- val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(defaultMatchingIssue)
}
@Test
fun getMatchingIssues_defaultMatchingIssue_unrelatedExtras_returnsListOfIssues() {
- val defaultMatchingIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID)
- val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val defaultMatchingIssue = issue("id1", MATCHING_GROUP_ID)
+ val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
issues = listOf(defaultMatchingIssue, nonMatchingIssue),
@@ -84,21 +91,21 @@ class SafetyCenterUiDataTest {
Bundle().apply {
putStringArrayList(
nonMatchingIssue.id,
- arrayListOf(NON_MATCHING_GROUP_ID)
+ arrayListOf(NON_MATCHING_GROUP_ID),
)
}
- )
+ ),
)
- val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(defaultMatchingIssue)
}
@Test
fun getMatchingIssues_mappingMatchingIssue_returnsListOfIssues() {
- val mappingMatchingIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID)
- val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val mappingMatchingIssue = issue("id1", NON_MATCHING_GROUP_ID)
+ val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
issues = listOf(mappingMatchingIssue, nonMatchingIssue),
@@ -107,51 +114,50 @@ class SafetyCenterUiDataTest {
Bundle().apply {
putStringArrayList(
mappingMatchingIssue.id,
- arrayListOf(MATCHING_GROUP_ID)
+ arrayListOf(MATCHING_GROUP_ID),
)
}
- )
+ ),
)
- val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(mappingMatchingIssue)
}
@Test
fun getMatchingIssues_noDefaultMatchingIssue_returnsEmptyList() {
- val nonMatchingIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID)
- val dismissedIssue = createSafetyCenterIssue("id2", MATCHING_GROUP_ID)
+ val nonMatchingIssue = issue("id1", NON_MATCHING_GROUP_ID)
+ val dismissedIssue = issue("id2", MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
issues = listOf(nonMatchingIssue),
- dismissedIssues = listOf(dismissedIssue)
+ dismissedIssues = listOf(dismissedIssue),
)
- val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID)
assertThat(result).isEmpty()
}
@Test
fun getMatchingDismissedIssues_defaultMatchingDismissedIssue_returnsListOfDismissedIssues() {
- val defaultMatchingDismissedIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID)
- val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val defaultMatchingDismissedIssue = issue("id1", MATCHING_GROUP_ID)
+ val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
dismissedIssues = listOf(defaultMatchingDismissedIssue, nonMatchingDismissedIssue)
)
- val result =
- SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(defaultMatchingDismissedIssue)
}
@Test
fun getMatchingDismissedIssues_defaultMatchingDismissedIssue2_returnsListOfDismissedIssues() {
- val defaultMatchingDismissedIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID)
- val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val defaultMatchingDismissedIssue = issue("id1", MATCHING_GROUP_ID)
+ val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
dismissedIssues = listOf(defaultMatchingDismissedIssue, nonMatchingDismissedIssue),
@@ -160,22 +166,21 @@ class SafetyCenterUiDataTest {
Bundle().apply {
putStringArrayList(
nonMatchingDismissedIssue.id,
- arrayListOf(NON_MATCHING_GROUP_ID)
+ arrayListOf(NON_MATCHING_GROUP_ID),
)
}
- )
+ ),
)
- val result =
- SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(defaultMatchingDismissedIssue)
}
@Test
fun getMatchingDismissedIssues_mappingMatchingDismissedIssue_returnsListOfDismissedIssues() {
- val mappingMatchingDismissedIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID)
- val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID)
+ val mappingMatchingDismissedIssue = issue("id1", NON_MATCHING_GROUP_ID)
+ val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
dismissedIssues = listOf(mappingMatchingDismissedIssue, nonMatchingDismissedIssue),
@@ -184,30 +189,28 @@ class SafetyCenterUiDataTest {
Bundle().apply {
putStringArrayList(
mappingMatchingDismissedIssue.id,
- arrayListOf(MATCHING_GROUP_ID)
+ arrayListOf(MATCHING_GROUP_ID),
)
}
- )
+ ),
)
- val result =
- SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(mappingMatchingDismissedIssue)
}
@Test
fun getMatchingDismissedIssues_noDefaultMatchingDismissedIssue_returnsEmptyList() {
- val nonMatchingDismissedIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID)
- val nonDismissedIssue = createSafetyCenterIssue("id2", MATCHING_GROUP_ID)
+ val nonMatchingDismissedIssue = issue("id1", NON_MATCHING_GROUP_ID)
+ val nonDismissedIssue = issue("id2", MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
issues = listOf(nonDismissedIssue),
- dismissedIssues = listOf(nonMatchingDismissedIssue)
+ dismissedIssues = listOf(nonMatchingDismissedIssue),
)
- val result =
- SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
assertThat(result).isEmpty()
}
@@ -215,24 +218,12 @@ class SafetyCenterUiDataTest {
@Test
fun getMatchingDismissedIssues_doesntReturnGreenIssues() {
val greenDismissedIssue =
- createSafetyCenterIssue(
- "id1",
- MATCHING_GROUP_ID,
- severityLevel = ISSUE_SEVERITY_LEVEL_OK
- )
+ issue("id1", MATCHING_GROUP_ID, severityLevel = ISSUE_SEVERITY_LEVEL_OK)
val yellowDismissedIssue =
- createSafetyCenterIssue(
- "id2",
- MATCHING_GROUP_ID,
- severityLevel = ISSUE_SEVERITY_LEVEL_RECOMMENDATION
- )
+ issue("id2", MATCHING_GROUP_ID, severityLevel = ISSUE_SEVERITY_LEVEL_RECOMMENDATION)
val redDismissedIssue =
- createSafetyCenterIssue(
- "id3",
- MATCHING_GROUP_ID,
- severityLevel = ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING
- )
- val nonMatchingDismissedIssue = createSafetyCenterIssue("id4", NON_MATCHING_GROUP_ID)
+ issue("id3", MATCHING_GROUP_ID, severityLevel = ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
+ val nonMatchingDismissedIssue = issue("id4", NON_MATCHING_GROUP_ID)
val safetyCenterData =
createSafetyCenterData(
dismissedIssues =
@@ -240,25 +231,82 @@ class SafetyCenterUiDataTest {
redDismissedIssue,
yellowDismissedIssue,
greenDismissedIssue,
- nonMatchingDismissedIssue
- ),
+ nonMatchingDismissedIssue,
+ )
)
- val result =
- SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
+ val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID)
assertThat(result).containsExactly(redDismissedIssue, yellowDismissedIssue).inOrder()
}
+ @Test
+ fun issueUiDatas_returnsIssueUiData() {
+ val issue1 = issue("id1", "group1")
+ val issue2 = issue("id2", "group2")
+ val safetyCenterData = createSafetyCenterData(listOf(issue1, issue2))
+
+ val result = uiData(safetyCenterData).issueUiDatas
+
+ assertThat(result)
+ .containsExactly(
+ IssueUiData(issue1, isDismissed = false),
+ IssueUiData(issue2, isDismissed = false),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun issueUiDatas_withResolvedIssues_returnsExpectedIssueUiData() {
+ val resolvedActionId = "actionId"
+ val resolvedIssue = issue("resolvedId", "group1")
+ val unresolvedIssue = issue("unresolvedId", "group2")
+ val safetyCenterData = createSafetyCenterData(listOf(resolvedIssue, unresolvedIssue))
+
+ val result =
+ uiData(safetyCenterData, resolvedIssues = mapOf(resolvedIssue.id to resolvedActionId))
+ .issueUiDatas
+
+ assertThat(result[0].resolvedIssueActionId).isEqualTo(resolvedActionId)
+ assertThat(result[1].resolvedIssueActionId).isNull()
+ }
+
+ @Test
+ fun issueUiDatas_withSameTaskSourceId_returnsExpectedIssueUiData() {
+ val taskId = 42
+ val sameTaskSourceId = "sameTaskSourceId"
+ val sameTaskIssue =
+ issueWithEncodedId(
+ encodeIssueId("sameTaskIssue", sourceId = sameTaskSourceId),
+ "group1",
+ )
+ val differentTaskIssue = issue("differentTaskIssue", "group2")
+ val safetyCenterData = createSafetyCenterData(listOf(sameTaskIssue, differentTaskIssue))
+
+ val result =
+ uiData(safetyCenterData, taskId, sameTaskSourceIds = listOf(sameTaskSourceId))
+ .issueUiDatas
+
+ assertThat(result[0].launchTaskId).isEqualTo(taskId)
+ assertThat(result[1].launchTaskId).isNull()
+ }
+
private companion object {
const val MATCHING_GROUP_ID = "matching_group_id"
const val NON_MATCHING_GROUP_ID = "non_matching_group_id"
+ private fun uiData(
+ safetyCenterData: SafetyCenterData,
+ taskId: Int = 0,
+ sameTaskSourceIds: List<String> = emptyList(),
+ resolvedIssues: Map<IssueId, ActionId> = emptyMap(),
+ ) = SafetyCenterUiData(safetyCenterData, taskId, sameTaskSourceIds, resolvedIssues)
+
fun createSafetyCenterData(
issues: List<SafetyCenterIssue> = listOf(),
entryGroups: List<SafetyCenterEntryGroup> = listOf(),
dismissedIssues: List<SafetyCenterIssue> = listOf(),
- extras: Bundle = Bundle()
+ extras: Bundle = Bundle(),
): SafetyCenterData {
val safetyCenterStatus =
SafetyCenterStatus.Builder("status title", "status summary").build()
@@ -276,20 +324,44 @@ class SafetyCenterUiDataTest {
return builder.build()
}
- fun createSafetyCenterEntryGroup(groupId: String) =
+ fun createSafetyCenterExtras(issuesToGroupsMapping: Bundle) =
+ Bundle().apply { putBundle(ISSUES_TO_GROUPS_BUNDLE_KEY, issuesToGroupsMapping) }
+
+ fun entryGroup(groupId: String) =
SafetyCenterEntryGroup.Builder(groupId, "group title").build()
- fun createSafetyCenterIssue(
+ fun issue(
issueId: String,
groupId: String,
- severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION
+ severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION,
+ ): SafetyCenterIssue = issueWithEncodedId(encodeIssueId(issueId), groupId, severityLevel)
+
+ private fun issueWithEncodedId(
+ encodedIssueId: String,
+ groupId: String,
+ severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION,
) =
- SafetyCenterIssue.Builder(issueId, "issue title", "issue summary")
+ SafetyCenterIssue.Builder(encodedIssueId, "issue title", "issue summary")
.setSeverityLevel(severityLevel)
.setGroupId(groupId)
.build()
- fun createSafetyCenterExtras(issuesToGroupsMapping: Bundle) =
- Bundle().apply { putBundle(ISSUES_TO_GROUPS_BUNDLE_KEY, issuesToGroupsMapping) }
+ fun encodeIssueId(
+ sourceIssueId: String,
+ sourceId: String = "defaultSource",
+ issueTypeId: String = "defaultIssueTypeId",
+ ): String =
+ SafetyCenterIds.encodeToString(
+ SafetyCenterIssueId.newBuilder()
+ .setSafetyCenterIssueKey(
+ SafetyCenterIssueKey.newBuilder()
+ .setSafetySourceId(sourceId)
+ .setSafetySourceIssueId(sourceIssueId)
+ .setUserId(UserHandle.myUserId())
+ .build()
+ )
+ .setIssueTypeId(issueTypeId)
+ .build()
+ )
}
}