1 /* 2 * Copyright (C) 2020 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.systemui.car.notification; 18 19 import android.os.RemoteException; 20 import android.util.ArraySet; 21 import android.util.Log; 22 23 import com.android.car.notification.AlertEntry; 24 import com.android.car.notification.NotificationDataManager; 25 import com.android.internal.statusbar.IStatusBarService; 26 import com.android.internal.statusbar.NotificationVisibility; 27 import com.android.systemui.dagger.SysUISingleton; 28 import com.android.systemui.dagger.qualifiers.UiBackground; 29 30 import java.util.List; 31 import java.util.Set; 32 import java.util.concurrent.Executor; 33 34 import javax.inject.Inject; 35 36 /** 37 * Handles notification logging, in particular, logging which notifications are visible and which 38 * are not. 39 */ 40 @SysUISingleton 41 public class NotificationVisibilityLogger { 42 43 private static final String TAG = "NotificationVisibilityLogger"; 44 45 private final ArraySet<NotificationVisibility> mCurrentlyVisible = new ArraySet<>(); 46 private final ArraySet<NotificationVisibility> mNewlyVisible = new ArraySet<>(); 47 private final ArraySet<NotificationVisibility> mPreviouslyVisible = new ArraySet<>(); 48 private final ArraySet<NotificationVisibility> mTmpCurrentlyVisible = new ArraySet<>(); 49 50 private final IStatusBarService mBarService; 51 private final Executor mUiBgExecutor; 52 private final NotificationDataManager mNotificationDataManager; 53 54 private boolean mIsVisible; 55 56 private final Runnable mVisibilityReporter = new Runnable() { 57 58 @Override 59 public void run() { 60 if (mIsVisible) { 61 List<AlertEntry> notifications = 62 mNotificationDataManager.getVisibleNotifications(); 63 int count = notifications.size(); 64 for (AlertEntry alertEntry : notifications) { 65 NotificationVisibility visObj = NotificationVisibility.obtain( 66 alertEntry.getKey(), 67 /* rank= */ -1, 68 count, 69 mIsVisible, 70 NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA); 71 mTmpCurrentlyVisible.add(visObj); 72 if (!mCurrentlyVisible.contains(visObj)) { 73 mNewlyVisible.add(visObj); 74 } 75 } 76 } 77 mPreviouslyVisible.addAll(mCurrentlyVisible); 78 mPreviouslyVisible.removeAll(mTmpCurrentlyVisible); 79 onNotificationVisibilityChanged(mNewlyVisible, mPreviouslyVisible); 80 81 recycleAllVisibilityObjects(mCurrentlyVisible); 82 mCurrentlyVisible.addAll(mTmpCurrentlyVisible); 83 84 recycleAllVisibilityObjects(mPreviouslyVisible); 85 recycleAllVisibilityObjects(mNewlyVisible); 86 recycleAllVisibilityObjects(mTmpCurrentlyVisible); 87 } 88 }; 89 90 @Inject NotificationVisibilityLogger( @iBackground Executor uiBgExecutor, IStatusBarService barService, NotificationDataManager notificationDataManager)91 public NotificationVisibilityLogger( 92 @UiBackground Executor uiBgExecutor, 93 IStatusBarService barService, 94 NotificationDataManager notificationDataManager) { 95 mUiBgExecutor = uiBgExecutor; 96 mBarService = barService; 97 mNotificationDataManager = notificationDataManager; 98 } 99 100 /** Triggers a visibility report update to be sent to StatusBarService. */ log(boolean isVisible)101 public void log(boolean isVisible) { 102 mIsVisible = isVisible; 103 mUiBgExecutor.execute(mVisibilityReporter); 104 } 105 106 /** Stops logging, clearing all visibility objects. */ stop()107 public void stop() { 108 recycleAllVisibilityObjects(mCurrentlyVisible); 109 } 110 111 /** 112 * Notify StatusBarService of change in notifications' visibility. 113 */ onNotificationVisibilityChanged( Set<NotificationVisibility> newlyVisible, Set<NotificationVisibility> noLongerVisible)114 private void onNotificationVisibilityChanged( 115 Set<NotificationVisibility> newlyVisible, Set<NotificationVisibility> noLongerVisible) { 116 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 117 return; 118 } 119 120 try { 121 mBarService.onNotificationVisibilityChanged( 122 cloneVisibilitiesAsArr(newlyVisible), cloneVisibilitiesAsArr(noLongerVisible)); 123 } catch (RemoteException e) { 124 // Won't fail unless the world has ended. 125 Log.e(TAG, "Failed to notify StatusBarService of notification visibility change"); 126 } 127 } 128 129 /** 130 * Clears array and recycles NotificationVisibility objects for reuse. 131 */ recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array)132 private static void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { 133 for (int i = 0; i < array.size(); i++) { 134 array.valueAt(i).recycle(); 135 } 136 array.clear(); 137 } 138 139 /** 140 * Converts Set of NotificationVisibility objects to primitive array. 141 */ cloneVisibilitiesAsArr(Set<NotificationVisibility> c)142 private static NotificationVisibility[] cloneVisibilitiesAsArr(Set<NotificationVisibility> c) { 143 NotificationVisibility[] array = new NotificationVisibility[c.size()]; 144 int i = 0; 145 for (NotificationVisibility nv : c) { 146 if (nv != null) { 147 array[i] = nv.clone(); 148 } 149 i++; 150 } 151 return array; 152 } 153 } 154