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