summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Dewey <deweytyl@google.com> 2025-02-19 17:16:10 +0000
committer Tyler Dewey <deweytyl@google.com> 2025-02-19 17:55:32 +0000
commitc80a38af51c9aaaee9b3bb675c0b2c0dc8fcf649 (patch)
tree5e82173eda40572926886774c09e044c8abee53b
parentce7c89a39a1303d9a9ee272d0c3f459190d782a3 (diff)
Add initial data layer for issues.
Will migrate usage to this gradually, starting with the new code for BC25. Will add support for dismissed issues in a followup CL. Bug: 379849463 Test: atest PermissionControllerMockingTests Flag: EXEMPT refactoring Relnote: N/a Change-Id: I8ccde6474185d1c7ed3cb1f49c260d619c49fef7
-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()
+ )
}
}