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