/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.car.notification; import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; import com.android.car.notification.AlertEntry; import com.android.car.notification.NotificationDataManager; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; /** * Handles notification logging, in particular, logging which notifications are visible and which * are not. */ @SysUISingleton public class NotificationVisibilityLogger { private static final String TAG = "NotificationVisibilityLogger"; private final ArraySet mCurrentlyVisible = new ArraySet<>(); private final ArraySet mNewlyVisible = new ArraySet<>(); private final ArraySet mPreviouslyVisible = new ArraySet<>(); private final ArraySet mTmpCurrentlyVisible = new ArraySet<>(); private final IStatusBarService mBarService; private final Executor mUiBgExecutor; private final NotificationDataManager mNotificationDataManager; private boolean mIsVisible; private final Runnable mVisibilityReporter = new Runnable() { @Override public void run() { if (mIsVisible) { List notifications = mNotificationDataManager.getVisibleNotifications(); int count = notifications.size(); for (AlertEntry alertEntry : notifications) { NotificationVisibility visObj = NotificationVisibility.obtain( alertEntry.getKey(), /* rank= */ -1, count, mIsVisible, NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA); mTmpCurrentlyVisible.add(visObj); if (!mCurrentlyVisible.contains(visObj)) { mNewlyVisible.add(visObj); } } } mPreviouslyVisible.addAll(mCurrentlyVisible); mPreviouslyVisible.removeAll(mTmpCurrentlyVisible); onNotificationVisibilityChanged(mNewlyVisible, mPreviouslyVisible); recycleAllVisibilityObjects(mCurrentlyVisible); mCurrentlyVisible.addAll(mTmpCurrentlyVisible); recycleAllVisibilityObjects(mPreviouslyVisible); recycleAllVisibilityObjects(mNewlyVisible); recycleAllVisibilityObjects(mTmpCurrentlyVisible); } }; @Inject public NotificationVisibilityLogger( @UiBackground Executor uiBgExecutor, IStatusBarService barService, NotificationDataManager notificationDataManager) { mUiBgExecutor = uiBgExecutor; mBarService = barService; mNotificationDataManager = notificationDataManager; } /** Triggers a visibility report update to be sent to StatusBarService. */ public void log(boolean isVisible) { mIsVisible = isVisible; mUiBgExecutor.execute(mVisibilityReporter); } /** Stops logging, clearing all visibility objects. */ public void stop() { recycleAllVisibilityObjects(mCurrentlyVisible); } /** * Notify StatusBarService of change in notifications' visibility. */ private void onNotificationVisibilityChanged( Set newlyVisible, Set noLongerVisible) { if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { return; } try { mBarService.onNotificationVisibilityChanged( cloneVisibilitiesAsArr(newlyVisible), cloneVisibilitiesAsArr(noLongerVisible)); } catch (RemoteException e) { // Won't fail unless the world has ended. Log.e(TAG, "Failed to notify StatusBarService of notification visibility change"); } } /** * Clears array and recycles NotificationVisibility objects for reuse. */ private static void recycleAllVisibilityObjects(ArraySet array) { for (int i = 0; i < array.size(); i++) { array.valueAt(i).recycle(); } array.clear(); } /** * Converts Set of NotificationVisibility objects to primitive array. */ private static NotificationVisibility[] cloneVisibilitiesAsArr(Set c) { NotificationVisibility[] array = new NotificationVisibility[c.size()]; int i = 0; for (NotificationVisibility nv : c) { if (nv != null) { array[i] = nv.clone(); } i++; } return array; } }