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