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.safetycenter.testing
18 
19 import android.app.Notification
20 import android.service.notification.StatusBarNotification
21 import com.android.safetycenter.internaldata.SafetyCenterIds
22 
23 /** The characteristic properties of a notification. */
24 data class NotificationCharacteristics(
25     val title: String? = null,
26     val text: String? = null,
27     val actions: List<CharSequence> = emptyList(),
28     val importance: Int = IMPORTANCE_ANY,
29     val blockable: Boolean? = null,
30     val safetySourceId: String? = null,
31 ) {
32     companion object {
33         const val IMPORTANCE_ANY = -1
34 
stringMatchesnull35         private fun stringMatches(actual: String?, expected: String?): Boolean {
36             return expected == null || actual == expected
37         }
38 
importanceMatchesnull39         private fun importanceMatches(
40             statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
41             characteristicImportance: Int
42         ): Boolean {
43             return characteristicImportance == IMPORTANCE_ANY ||
44                 statusBarNotificationWithChannel.channel.importance == characteristicImportance
45         }
46 
blockableMatchesnull47         private fun blockableMatches(
48             statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
49             characteristicBlockable: Boolean?
50         ): Boolean {
51             return characteristicBlockable == null ||
52                 statusBarNotificationWithChannel.channel.isBlockable == characteristicBlockable
53         }
54 
safetySourceIdMatchesnull55         fun safetySourceIdMatches(
56             statusBarNotification: StatusBarNotification,
57             safetySourceId: String?
58         ): Boolean {
59             return safetySourceId == null ||
60                 SafetyCenterIds.issueKeyFromString(statusBarNotification.tag).safetySourceId ==
61                     safetySourceId
62         }
63 
isMatchnull64         private fun isMatch(
65             statusBarNotificationWithChannel: StatusBarNotificationWithChannel,
66             characteristic: NotificationCharacteristics
67         ): Boolean {
68             val notif = statusBarNotificationWithChannel.statusBarNotification.notification
69             val extras = notif.extras
70             return notif != null &&
71                 stringMatches(extras.getString(Notification.EXTRA_TITLE), characteristic.title) &&
72                 stringMatches(extras.getString(Notification.EXTRA_TEXT), characteristic.text) &&
73                 notif.actions.orEmpty().map { it.title } == characteristic.actions &&
74                 importanceMatches(statusBarNotificationWithChannel, characteristic.importance) &&
75                 blockableMatches(statusBarNotificationWithChannel, characteristic.blockable) &&
76                 safetySourceIdMatches(
77                     statusBarNotificationWithChannel.statusBarNotification,
78                     characteristic.safetySourceId
79                 )
80         }
81 
areMatchingnull82         fun areMatching(
83             statusBarNotifications: List<StatusBarNotificationWithChannel>,
84             characteristics: List<NotificationCharacteristics>
85         ): Boolean {
86             if (statusBarNotifications.size != characteristics.size) {
87                 return false
88             }
89             return statusBarNotifications.zip(characteristics).all { isMatch(it.first, it.second) }
90         }
91     }
92 }
93