1 /* 2 * Copyright (C) 2022 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.car.notification; 18 19 import android.os.RemoteException; 20 import android.util.ArraySet; 21 import android.util.Log; 22 23 import com.android.internal.statusbar.IStatusBarService; 24 import com.android.internal.statusbar.NotificationVisibility; 25 26 import java.util.List; 27 import java.util.Set; 28 import java.util.stream.Collectors; 29 30 /** 31 * Handles notification logging, in particular, logging which notifications are visible and which 32 * are not. 33 */ 34 public class CarNotificationVisibilityLogger { 35 36 private static final String TAG = "CarNotifVisLogger"; 37 38 private final ArraySet<NotificationVisibility> mCurrentlyVisible = new ArraySet<>(); 39 40 private final NotificationDataManager mNotificationDataManager; 41 private IStatusBarService mStatusBarService; 42 CarNotificationVisibilityLogger(IStatusBarService statusBarService, NotificationDataManager notificationDataManager)43 CarNotificationVisibilityLogger(IStatusBarService statusBarService, 44 NotificationDataManager notificationDataManager) { 45 mStatusBarService = statusBarService; 46 mNotificationDataManager = notificationDataManager; 47 } 48 obtainNotificationVisibility(AlertEntry entry, int count)49 private NotificationVisibility obtainNotificationVisibility(AlertEntry entry, int count) { 50 return NotificationVisibility.obtain( 51 entry.getKey(), 52 /* rank= */ -1, 53 count, 54 /* isVisible= */ true, 55 NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA); 56 } 57 58 /** 59 * Notifies the appropriate services that the notification state might have changed. 60 * 61 * @param isVisible Whether the notification panel is visible or not. If it is false the 62 * method assumes that all the notifications are invisible. 63 */ notifyVisibilityChanged(boolean isVisible)64 public void notifyVisibilityChanged(boolean isVisible) { 65 ArraySet<NotificationVisibility> previouslyVisible = new ArraySet<>(mCurrentlyVisible); 66 67 ArraySet<NotificationVisibility> newlyVisible = new ArraySet<>(); 68 ArraySet<NotificationVisibility> noLongerVisible = new ArraySet<>(); 69 70 mCurrentlyVisible.clear(); 71 72 if (isVisible) { 73 List<AlertEntry> entries = mNotificationDataManager.getVisibleNotifications(); 74 int count = entries.size(); 75 76 mCurrentlyVisible.addAll( 77 entries.stream().map(entry -> obtainNotificationVisibility(entry, count)) 78 .collect(Collectors.toSet())); 79 80 newlyVisible.addAll(mCurrentlyVisible); 81 newlyVisible.removeAll(previouslyVisible); 82 83 noLongerVisible.addAll(previouslyVisible); 84 noLongerVisible.removeAll(mCurrentlyVisible); 85 } else { 86 noLongerVisible.addAll(previouslyVisible); 87 } 88 89 onNotificationVisibilityChanged(newlyVisible, previouslyVisible); 90 recycleAndClear(noLongerVisible); 91 } 92 93 /** 94 * Notify StatusBarService of change in notifications' visibility. 95 * 96 * @param newlyVisible Notifications that became visible. 97 * @param noLongerVisible Notifications that are no longer visible. 98 */ onNotificationVisibilityChanged( Set<NotificationVisibility> newlyVisible, Set<NotificationVisibility> noLongerVisible)99 private void onNotificationVisibilityChanged( 100 Set<NotificationVisibility> newlyVisible, Set<NotificationVisibility> noLongerVisible) { 101 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 102 return; 103 } 104 105 try { 106 NotificationVisibility[] newlyVisibleArray = createDeepClone(newlyVisible) 107 .toArray(new NotificationVisibility[0]); 108 109 NotificationVisibility[] noLongerVisibleArray = createDeepClone(noLongerVisible) 110 .toArray(new NotificationVisibility[0]); 111 112 mStatusBarService.onNotificationVisibilityChanged(newlyVisibleArray, 113 noLongerVisibleArray); 114 } catch (RemoteException e) { 115 Log.e(TAG, "Failed to notify StatusBarService of notification visibility change"); 116 } 117 } 118 119 /** 120 * Clears array and recycles NotificationVisibility objects for reuse. 121 * 122 * @param set The array that needs to be cleared. 123 */ recycleAndClear(Set<NotificationVisibility> set)124 private static void recycleAndClear(Set<NotificationVisibility> set) { 125 set.stream().forEach(NotificationVisibility::recycle); 126 set.clear(); 127 } 128 129 /** 130 * Creates a deep clone of the collection by cloning each item of the collection. 131 * 132 * @param set The collection that has to be cloned. 133 */ createDeepClone( Set<NotificationVisibility> set)134 private static Set<NotificationVisibility> createDeepClone( 135 Set<NotificationVisibility> set) { 136 return set.stream().map(NotificationVisibility::clone).collect(Collectors.toSet()); 137 } 138 } 139