Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50d11bf0 authored by Ned Burns's avatar Ned Burns Committed by Android (Google) Code Review
Browse files

Merge "Create NotificationListController"

parents df1f806c f36c6255
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -33,6 +33,13 @@ public interface NotificationEntryListener {
    default void onPendingEntryAdded(NotificationEntry entry) {
    }

    // TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar
    /**
     * Called when a new entry is created but before it has been filtered or displayed to the user.
     */
    default void onBeforeNotificationAdded(NotificationEntry entry) {
    }

    /**
     * Called when a new entry is created.
     */
+7 −48
Original line number Diff line number Diff line
@@ -18,11 +18,9 @@ package com.android.systemui.statusbar.notification;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
@@ -41,7 +39,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;

@@ -68,8 +65,6 @@ public class NotificationEntryManager implements
    @VisibleForTesting
    protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();

    private final DeviceProvisionedController mDeviceProvisionedController =
            Dependency.get(DeviceProvisionedController.class);
    private final ForegroundServiceController mForegroundServiceController =
            Dependency.get(ForegroundServiceController.class);

@@ -81,25 +76,12 @@ public class NotificationEntryManager implements
    private NotificationListenerService.RankingMap mLatestRankingMap;
    @VisibleForTesting
    protected NotificationData mNotificationData;
    private NotificationListContainer mListContainer;

    @VisibleForTesting
    final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
            = new ArrayList<>();
    private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();

    private final DeviceProvisionedController.DeviceProvisionedListener
            mDeviceProvisionedListener =
            new DeviceProvisionedController.DeviceProvisionedListener() {
                @Override
                public void onDeviceProvisionedChanged() {
                    updateNotifications();
                }
            };

    public void destroy() {
        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("NotificationEntryManager state:");
@@ -151,9 +133,6 @@ public class NotificationEntryManager implements
            HeadsUpManager headsUpManager) {
        mPresenter = presenter;
        mNotificationData.setHeadsUpManager(headsUpManager);
        mListContainer = listContainer;

        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
    }

    /** Adds multiple {@link NotificationLifetimeExtender}s. */
@@ -227,7 +206,9 @@ public class NotificationEntryManager implements
                    listener.onEntryInflated(entry, inflatedFlags);
                }
                mNotificationData.add(entry);
                tagForeground(entry.notification);
                for (NotificationEntryListener listener : mNotificationEntryListeners) {
                    listener.onBeforeNotificationAdded(entry);
                }
                updateNotifications();
                for (NotificationEntryListener listener : mNotificationEntryListeners) {
                    listener.onNotificationAdded(entry);
@@ -283,7 +264,6 @@ public class NotificationEntryManager implements

                if (entry.rowExists()) {
                    entry.removeRow();
                    mListContainer.cleanUpViewStateForEntry(entry);
                }

                // Let's remove the children if this was a summary
@@ -368,19 +348,6 @@ public class NotificationEntryManager implements
        }
    }

    @VisibleForTesting
    void tagForeground(StatusBarNotification notification) {
        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
                notification.getUserId(), notification.getPackageName());
        if (activeOps != null) {
            int N = activeOps.size();
            for (int i = 0; i < N; i++) {
                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
                        notification.getPackageName(), true);
            }
        }
    }

    @Override
    public void addNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
@@ -391,15 +358,6 @@ public class NotificationEntryManager implements
        }
    }

    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
        String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
                UserHandle.getUserId(uid), pkg);
        if (foregroundKey != null) {
            mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
            updateNotifications();
        }
    }

    private void updateNotificationInternal(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) throws InflationException {
        if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
@@ -452,9 +410,10 @@ public class NotificationEntryManager implements

    public void updateNotifications() {
        mNotificationData.filterAndSort();

        if (mPresenter != null) {
            mPresenter.updateNotificationViews();
        }
    }

    public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
        List<NotificationEntry> entries = new ArrayList<>();
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.statusbar.notification;

import static com.android.internal.util.Preconditions.checkNotNull;

import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;

import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;

/**
 * Root controller for the list of notifications in the shade.
 *
 * TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy
 * domain-specific behavior (ARC, etc) to subcontrollers.
 */
public class NotificationListController {
    private final NotificationEntryManager mEntryManager;
    private final NotificationListContainer mListContainer;
    private final ForegroundServiceController mForegroundServiceController;
    private final DeviceProvisionedController mDeviceProvisionedController;

    public NotificationListController(
            NotificationEntryManager entryManager,
            NotificationListContainer listContainer,
            ForegroundServiceController foregroundServiceController,
            DeviceProvisionedController deviceProvisionedController) {
        mEntryManager = checkNotNull(entryManager);
        mListContainer = checkNotNull(listContainer);
        mForegroundServiceController = checkNotNull(foregroundServiceController);
        mDeviceProvisionedController = checkNotNull(deviceProvisionedController);
    }

    /**
     * Causes the controller to register listeners on its dependencies. This method must be called
     * before the controller is ready to perform its duties.
     */
    public void bind() {
        mEntryManager.addNotificationEntryListener(mEntryListener);
        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
    }

    /** Should be called when the list controller is being destroyed. */
    public void destroy() {
        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
    }

    @SuppressWarnings("FieldCanBeLocal")
    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
        @Override
        public void onEntryRemoved(
                NotificationEntry entry,
                NotificationVisibility visibility,
                boolean removedByUser) {
            mListContainer.cleanUpViewStateForEntry(entry);
        }

        @Override
        public void onBeforeNotificationAdded(NotificationEntry entry) {
            tagForeground(entry.notification);
        }
    };

    private final DeviceProvisionedListener mDeviceProvisionedListener =
            new DeviceProvisionedListener() {
                @Override
                public void onDeviceProvisionedChanged() {
                    mEntryManager.updateNotifications();
                }
            };

    // TODO: This method is horrifically inefficient
    private void tagForeground(StatusBarNotification notification) {
        ArraySet<Integer> activeOps =
                mForegroundServiceController.getAppOps(
                        notification.getUserId(), notification.getPackageName());
        if (activeOps != null) {
            int len = activeOps.size();
            for (int i = 0; i < len; i++) {
                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
                        notification.getPackageName(), true);
            }
        }
    }

    /** When an app op changes, propagate that change to notifications. */
    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
        String foregroundKey =
                mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg);
        if (foregroundKey != null) {
            mEntryManager
                    .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
            mEntryManager.updateNotifications();
        }
    }
}
+12 −2
Original line number Diff line number Diff line
@@ -196,6 +196,7 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationListController;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -387,6 +388,7 @@ public class StatusBar extends SystemUI implements DemoMode,
    private NotificationGutsManager mGutsManager;
    protected NotificationLogger mNotificationLogger;
    protected NotificationEntryManager mEntryManager;
    private NotificationListController mNotificationListController;
    private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
    private NotificationRowBinder mNotificationRowBinder;
    protected NotificationViewHierarchyManager mViewHierarchyManager;
@@ -593,7 +595,7 @@ public class StatusBar extends SystemUI implements DemoMode,
    public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
        mForegroundServiceController.onAppOpChanged(code, uid, packageName, active);
        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
            mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
            mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active);
        });
    }

@@ -1044,6 +1046,13 @@ public class StatusBar extends SystemUI implements DemoMode,
                mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
                mNotificationAlertingManager);

        mNotificationListController =
                new NotificationListController(
                        mEntryManager,
                        (NotificationListContainer) mStackScroller,
                        mForegroundServiceController,
                        mDeviceProvisionedController);

        mAppOpsController.addCallback(APP_OPS, this);
        mNotificationListener.setUpWithPresenter(mPresenter);
        mNotificationShelf.setOnActivatedListener(mPresenter);
@@ -1056,6 +1065,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                this, Dependency.get(BubbleController.class), mNotificationActivityStarter));

        mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
        mNotificationListController.bind();
    }

    /**
@@ -2831,7 +2841,7 @@ public class StatusBar extends SystemUI implements DemoMode,
        } catch (RemoteException e) {
            // Ignore.
        }
        mEntryManager.destroy();
        mNotificationListController.destroy();
        // End old BaseStatusBar.destroy().
        if (mStatusBarWindow != null) {
            mWindowManager.removeViewImmediate(mStatusBarWindow);
+0 −92
Original line number Diff line number Diff line
@@ -22,19 +22,15 @@ import static junit.framework.Assert.assertTrue;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -49,7 +45,6 @@ import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
import android.widget.FrameLayout;

import com.android.internal.logging.MetricsLogger;
@@ -79,8 +74,6 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -346,7 +339,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {

        verify(mEntryListener, never()).onInflationError(any(), any());

        verify(mListContainer).cleanUpViewStateForEntry(mEntry);
        verify(mPresenter).updateNotificationViews();
        verify(mEntryListener).onEntryRemoved(
                mEntry, null, false /* removedByUser */);
@@ -400,90 +392,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
                mEntry, null, false /* removedByUser */);
    }

    @Test
    public void testUpdateAppOps_foregroundNoti() {
        com.android.systemui.util.Assert.isNotMainThread();

        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                .thenReturn(mEntry.key);
        mEntry.setRow(mRow);
        mEntryManager.getNotificationData().add(mEntry);

        mEntryManager.updateNotificationsForAppOp(
                AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
                mEntry.notification.getPackageName(), true);

        verify(mPresenter, times(1)).updateNotificationViews();
        assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
                AppOpsManager.OP_CAMERA));
    }

    @Test
    public void testUpdateAppOps_otherNoti() {
        com.android.systemui.util.Assert.isNotMainThread();

        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                .thenReturn(null);
        mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);

        verify(mPresenter, never()).updateNotificationViews();
    }

    @Test
    public void testAddNotificationExistingAppOps() {
        mEntry.setRow(mRow);
        mEntryManager.getNotificationData().add(mEntry);
        ArraySet<Integer> expected = new ArraySet<>();
        expected.add(3);
        expected.add(235);
        expected.add(1);

        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn(expected);
        when(mForegroundServiceController.getStandardLayoutKey(
                mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn(mEntry.key);

        mEntryManager.tagForeground(mEntry.notification);

        Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
        for (int op : expected) {
            assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
        }
    }

    @Test
    public void testAdd_noExistingAppOps() {
        mEntry.setRow(mRow);
        mEntryManager.getNotificationData().add(mEntry);
        when(mForegroundServiceController.getStandardLayoutKey(
                mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn(null);

        mEntryManager.tagForeground(mEntry.notification);
        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
    }

    @Test
    public void testAdd_existingAppOpsNotForegroundNoti() {
        mEntry.setRow(mRow);
        mEntryManager.getNotificationData().add(mEntry);
        ArraySet<Integer> ops = new ArraySet<>();
        ops.add(3);
        ops.add(235);
        ops.add(1);
        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn(ops);
        when(mForegroundServiceController.getStandardLayoutKey(
                mEntry.notification.getUserId(),
                mEntry.notification.getPackageName())).thenReturn("something else");

        mEntryManager.tagForeground(mEntry.notification);
        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
    }

    @Test
    public void testUpdateNotificationRanking() {
        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
Loading