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