1 /* 2 * Copyright (C) 2018 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 package com.android.car.notification; 17 18 import android.app.Notification; 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.service.notification.StatusBarNotification; 22 23 import androidx.recyclerview.widget.DiffUtil; 24 25 import java.util.List; 26 import java.util.Objects; 27 28 /** 29 * {@link DiffUtil} for car notifications. 30 * This class is not intended for general usage except for the static methods. 31 * 32 * <p> Two notifications are considered the same if they have the same: 33 * <ol> 34 * <li> GroupKey 35 * <li> Number of StatusBarNotifications contained 36 * <li> The order of each StatusBarNotification 37 * <li> The identifier of each individual StatusBarNotification contained 38 * <li> The content of each individual StatusBarNotification contained 39 * </ol> 40 */ 41 class CarNotificationDiff extends DiffUtil.Callback { 42 private final Context mContext; 43 private final List<NotificationGroup> mOldList; 44 private final List<NotificationGroup> mNewList; 45 CarNotificationDiff( Context context, List<NotificationGroup> oldList, List<NotificationGroup> newList)46 CarNotificationDiff( 47 Context context, 48 List<NotificationGroup> oldList, 49 List<NotificationGroup> newList) { 50 mContext = context; 51 mOldList = oldList; 52 mNewList = newList; 53 } 54 55 @Override getOldListSize()56 public int getOldListSize() { 57 return mOldList.size(); 58 } 59 60 @Override getNewListSize()61 public int getNewListSize() { 62 return mNewList.size(); 63 } 64 65 @Override areItemsTheSame(int oldItemPosition, int newItemPosition)66 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { 67 NotificationGroup oldItem = mOldList.get(oldItemPosition); 68 NotificationGroup newItem = mNewList.get(newItemPosition); 69 return sameGroupUniqueIdentifiers(oldItem, newItem); 70 } 71 72 /** 73 * Shallow comparison for {@link NotificationGroup}. 74 * <p> 75 * Checks if two grouped notifications have the same: 76 * <ol> 77 * <li> GroupKey 78 * <li> GroupSummaryKey 79 * </ol> 80 * <p> 81 * Checks for individual StatusBarNotification contained is not done because child will itself 82 * take care of it. 83 */ sameGroupUniqueIdentifiers( NotificationGroup oldItem, NotificationGroup newItem)84 static boolean sameGroupUniqueIdentifiers( 85 NotificationGroup oldItem, NotificationGroup newItem) { 86 87 if (oldItem == newItem) { 88 return true; 89 } 90 91 if (!oldItem.getGroupKey().equals(newItem.getGroupKey())) { 92 return false; 93 } 94 95 if (!sameNotificationKey( 96 oldItem.getGroupSummaryNotification(), newItem.getGroupSummaryNotification())) { 97 return false; 98 } 99 100 return true; 101 } 102 103 /** 104 * Shallow comparison for {@link StatusBarNotification}: only comparing the unique IDs. 105 * 106 * <p> Returns true if two notifications have the same key. 107 */ sameNotificationKey( StatusBarNotification oldItem, StatusBarNotification newItem)108 static boolean sameNotificationKey( 109 StatusBarNotification oldItem, StatusBarNotification newItem) { 110 if (oldItem == newItem) { 111 return true; 112 } 113 114 return oldItem != null 115 && newItem != null 116 && Objects.equals(oldItem.getKey(), newItem.getKey()); 117 } 118 119 /** 120 * Shallow comparison for {@link StatusBarNotification}: comparing the unique IDs and the 121 * notification Flags. 122 * 123 * <p> Returns true if two notifications have the same key and notification flags. 124 */ sameNotificationKeyAndFlags( StatusBarNotification oldItem, StatusBarNotification newItem)125 static boolean sameNotificationKeyAndFlags( 126 StatusBarNotification oldItem, StatusBarNotification newItem) { 127 return sameNotificationKey(oldItem, newItem) 128 && oldItem.getNotification().flags == newItem.getNotification().flags; 129 } 130 131 /** 132 * Deep comparison for {@link NotificationGroup}. 133 * 134 * <p> Compare the size and contents of each StatusBarNotification inside the NotificationGroup. 135 * 136 * <p> This method will only be called if {@link #areItemsTheSame} returns true. 137 */ 138 @Override areContentsTheSame(int oldItemPosition, int newItemPosition)139 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { 140 NotificationGroup oldItem = mOldList.get(oldItemPosition); 141 NotificationGroup newItem = mNewList.get(newItemPosition); 142 143 if (!sameNotificationContent( 144 oldItem.getGroupSummaryNotification(), newItem.getGroupSummaryNotification())) { 145 return false; 146 } 147 148 if (oldItem.getChildCount() != newItem.getChildCount()) { 149 return false; 150 } 151 152 List<StatusBarNotification> oldChildNotifications = oldItem.getChildNotifications(); 153 List<StatusBarNotification> newChildNotifications = newItem.getChildNotifications(); 154 155 for (int i = 0; i < oldItem.getChildCount(); i++) { 156 StatusBarNotification oldNotification = oldChildNotifications.get(i); 157 StatusBarNotification newNotification = newChildNotifications.get(i); 158 if (!sameNotificationContent(oldNotification, newNotification)) { 159 return false; 160 } 161 } 162 163 return true; 164 } 165 166 /** 167 * Deep comparison for {@link StatusBarNotification}. 168 * 169 * <p> We are only comparing a subset of the fields that have visible effects on our product. 170 * Most of the deprecated fields are not compared. 171 * Fields that do not have visible effects, e.g. privacy-related things are ignored for now. 172 */ sameNotificationContent( StatusBarNotification oldItem, StatusBarNotification newItem)173 private boolean sameNotificationContent( 174 StatusBarNotification oldItem, StatusBarNotification newItem) { 175 176 if (oldItem == newItem) { 177 return true; 178 } 179 180 if (oldItem == null || newItem == null) { 181 return false; 182 } 183 184 if (oldItem.isGroup() != newItem.isGroup() 185 || oldItem.isClearable() != newItem.isClearable() 186 || oldItem.isOngoing() != newItem.isOngoing()) { 187 return false; 188 } 189 190 Notification oldNotification = oldItem.getNotification(); 191 Notification newNotification = newItem.getNotification(); 192 193 if (oldNotification.flags != newNotification.flags 194 || oldNotification.category != newNotification.category 195 || oldNotification.color != newNotification.color 196 || !areBundlesEqual(oldNotification.extras, newNotification.extras) 197 || !Objects.equals(oldNotification.contentIntent, newNotification.contentIntent) 198 || !Objects.equals(oldNotification.deleteIntent, newNotification.deleteIntent) 199 || !Objects.equals( 200 oldNotification.fullScreenIntent, newNotification.fullScreenIntent) 201 || !Objects.deepEquals(oldNotification.actions, newNotification.actions)) { 202 return false; 203 } 204 205 // Recover builders only until the above if-statements fail 206 Notification.Builder oldBuilder = 207 Notification.Builder.recoverBuilder(mContext, oldNotification); 208 Notification.Builder newBuilder = 209 Notification.Builder.recoverBuilder(mContext, newNotification); 210 211 return !Notification.areStyledNotificationsVisiblyDifferent(oldBuilder, newBuilder); 212 } 213 areBundlesEqual(Bundle oldBundle, Bundle newBundle)214 private boolean areBundlesEqual(Bundle oldBundle, Bundle newBundle) { 215 if (oldBundle.size() != newBundle.size()) { 216 return false; 217 } 218 219 for (String key : oldBundle.keySet()) { 220 if (!newBundle.containsKey(key)) { 221 return false; 222 } 223 224 Object oldValue = oldBundle.get(key); 225 Object newValue = newBundle.get(key); 226 if (!Objects.equals(oldValue, newValue)) { 227 return false; 228 } 229 } 230 231 return true; 232 } 233 } 234