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