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

Commit 62232b6c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Properly end lifetimeExtention for ongoing notifications"

parents 2648ce46 b4ecc576
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -257,6 +257,30 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
        return !canRemoveImmediately(entry.getKey());
    }

    /**
     * @param key
     * @return true if the entry is pinned
     */
    public boolean isSticky(String key) {
        AlertEntry alerting = mAlertEntries.get(key);
        if (alerting != null) {
            return alerting.isSticky();
        }
        return false;
    }

    /**
     * @param key
     * @return When a HUN entry should be removed in milliseconds from now
     */
    public long getEarliestRemovalTime(String key) {
        AlertEntry alerting = mAlertEntries.get(key);
        if (alerting != null) {
            return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis());
        }
        return 0;
    }

    @Override
    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
        if (shouldExtend) {
+33 −23
Original line number Diff line number Diff line
@@ -19,9 +19,12 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;

import android.util.ArraySet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -38,8 +41,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;

import java.util.Objects;
import com.android.systemui.util.concurrency.DelayableExecutor;

import javax.inject.Inject;

@@ -66,12 +68,11 @@ public class HeadsUpCoordinator implements Coordinator {
    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
    private final NotificationRemoteInputManager mRemoteInputManager;
    private final NodeController mIncomingHeaderController;

    // tracks the current HeadUpNotification reported by HeadsUpManager
    private @Nullable NotificationEntry mCurrentHun;
    private final DelayableExecutor mExecutor;

    private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
    private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
    // notifs we've extended the lifetime for
    private final ArraySet<NotificationEntry> mNotifsExtendingLifetime = new ArraySet<>();

    @Inject
    public HeadsUpCoordinator(
@@ -79,12 +80,14 @@ public class HeadsUpCoordinator implements Coordinator {
            HeadsUpViewBinder headsUpViewBinder,
            NotificationInterruptStateProvider notificationInterruptStateProvider,
            NotificationRemoteInputManager remoteInputManager,
            @IncomingHeader NodeController incomingHeaderController) {
            @IncomingHeader NodeController incomingHeaderController,
            @Main DelayableExecutor executor) {
        mHeadsUpManager = headsUpManager;
        mHeadsUpViewBinder = headsUpViewBinder;
        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
        mRemoteInputManager = remoteInputManager;
        mIncomingHeaderController = incomingHeaderController;
        mExecutor = executor;
    }

    @Override
@@ -178,16 +181,26 @@ public class HeadsUpCoordinator implements Coordinator {
        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) {
            boolean isShowingHun = isCurrentlyShowingHun(entry);
            if (isShowingHun) {
                mNotifExtendingLifetime = entry;
                if (isSticky(entry)) {
                    long removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
                    if (removeAfterMillis <= 0) return false;
                    mExecutor.executeDelayed(() -> {
                        // make sure that the entry was not updated
                        long removeAfterMillis2 =
                                mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
                        if (mNotifsExtendingLifetime.contains(entry) && removeAfterMillis2 <= 0) {
                            mHeadsUpManager.removeNotification(entry.getKey(), true);
                        }
                    }, removeAfterMillis);
                }
                mNotifsExtendingLifetime.add(entry);
            }
            return isShowingHun;
        }

        @Override
        public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
            if (Objects.equals(mNotifExtendingLifetime, entry)) {
                mNotifExtendingLifetime = null;
            }
            mNotifsExtendingLifetime.remove(entry);
        }
    };

@@ -220,27 +233,24 @@ public class HeadsUpCoordinator implements Coordinator {
            new OnHeadsUpChangedListener() {
        @Override
        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
            NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
            if (!Objects.equals(mCurrentHun, newHUN)) {
                mCurrentHun = newHUN;
                endNotifLifetimeExtension();
            }
            if (!isHeadsUp) {
                mHeadsUpViewBinder.unbindHeadsUpView(entry);
                endNotifLifetimeExtensionIfExtended(entry);
            }
        }
    };

    private boolean isSticky(NotificationEntry entry) {
        return mHeadsUpManager.isSticky(entry.getKey());
    }

    private boolean isCurrentlyShowingHun(ListEntry entry) {
        return mCurrentHun == entry.getRepresentativeEntry();
        return mHeadsUpManager.isAlerting(entry.getKey());
    }

    private void endNotifLifetimeExtension() {
        if (mNotifExtendingLifetime != null) {
            mEndLifetimeExtension.onEndLifetimeExtension(
                    mLifetimeExtender,
                    mNotifExtendingLifetime);
            mNotifExtendingLifetime = null;
    private void endNotifLifetimeExtensionIfExtended(NotificationEntry entry) {
        if (mNotifsExtendingLifetime.remove(entry)) {
            mEndLifetimeExtension.onEndLifetimeExtension(mLifetimeExtender, entry);
        }
    }
}
+61 −42
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -44,6 +47,8 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.Before;
import org.junit.Test;
@@ -52,6 +57,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -75,6 +82,9 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
    @Mock private NodeController mHeaderController;

    private NotificationEntry mEntry;
    private final FakeSystemClock mClock = new FakeSystemClock();
    private final FakeExecutor mExecutor = new FakeExecutor(mClock);
    private final ArrayList<NotificationEntry> mHuns = new ArrayList();

    @Before
    public void setUp() {
@@ -85,7 +95,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
                mHeadsUpViewBinder,
                mNotificationInterruptStateProvider,
                mRemoteInputManager,
                mHeaderController);
                mHeaderController,
                mExecutor);

        mCoordinator.attach(mNotifPipeline);

@@ -105,6 +116,16 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
                notifLifetimeExtenderCaptor.capture());
        verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());

        given(mHeadsUpManager.getAllEntries()).willAnswer(i -> mHuns.stream());
        given(mHeadsUpManager.isAlerting(anyString())).willAnswer(i -> {
            String key = i.getArgument(0);
            for (NotificationEntry entry : mHuns) {
                if (entry.getKey().equals(key)) return true;
            }
            return false;
        });
        when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L);

        mCollectionListener = notifCollectionCaptor.getValue();
        mNotifPromoter = notifPromoterCaptor.getValue();
        mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
@@ -115,64 +136,65 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
        mEntry = new NotificationEntryBuilder().build();
    }

    @Test
    public void testCancelStickyNotification() {
        when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
        addHUN(mEntry);
        when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L);
        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0));
        mClock.advanceTime(1000L);
        mExecutor.runAllReady();
        verify(mHeadsUpManager, times(1))
                .removeNotification(anyString(), eq(true));
    }

    @Test
    public void testCancelUpdatedStickyNotification() {
        when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
        addHUN(mEntry);
        when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0));
        mClock.advanceTime(1000L);
        mExecutor.runAllReady();
        verify(mHeadsUpManager, times(0))
                .removeNotification(anyString(), eq(true));
    }

    @Test
    public void testPromotesCurrentHUN() {
        // GIVEN the current HUN is set to mEntry
        setCurrentHUN(mEntry);
        addHUN(mEntry);

        // THEN only promote the current HUN, mEntry
        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
        assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
        assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder()
                .setPkg("test-package2")
                .build()));
    }

    @Test
    public void testIncludeInSectionCurrentHUN() {
        // GIVEN the current HUN is set to mEntry
        setCurrentHUN(mEntry);
        addHUN(mEntry);

        // THEN only section the current HUN, mEntry
        assertTrue(mNotifSectioner.isInSection(mEntry));
        assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build()));
        assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder()
                .setPkg("test-package")
                .build()));
    }

    @Test
    public void testLifetimeExtendsCurrentHUN() {
        // GIVEN there is a HUN, mEntry
        setCurrentHUN(mEntry);
        addHUN(mEntry);

        // THEN only the current HUN, mEntry, should be lifetimeExtended
        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
        assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
                new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
    }

    @Test
    public void testLifetimeExtensionEndsOnNewHUN() {
        // GIVEN there was a HUN that was lifetime extended
        setCurrentHUN(mEntry);
        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
                mEntry, /* cancellation reason */ 0));

        // WHEN there's a new HUN
        NotificationEntry newHUN = new NotificationEntryBuilder().build();
        setCurrentHUN(newHUN);

        // THEN the old entry's lifetime extension should be cancelled
        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
    }

    @Test
    public void testLifetimeExtensionEndsOnNoHUNs() {
        // GIVEN there was a HUN that was lifetime extended
        setCurrentHUN(mEntry);
        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
                mEntry, /* cancellation reason */ 0));

        // WHEN there's no longer a HUN
        setCurrentHUN(null);

        // THEN the old entry's lifetime extension should be cancelled
        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
                new NotificationEntryBuilder()
                        .setPkg("test-package")
                        .build(), /* cancellationReason */ 0));
    }

    @Test
@@ -208,7 +230,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
    @Test
    public void testOnEntryRemovedRemovesHeadsUpNotification() {
        // GIVEN the current HUN is mEntry
        setCurrentHUN(mEntry);
        addHUN(mEntry);

        // WHEN mEntry is removed from the notification collection
        mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
@@ -218,12 +240,9 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
        verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
    }

    private void setCurrentHUN(NotificationEntry entry) {
    private void addHUN(NotificationEntry entry) {
        mHuns.add(entry);
        when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
        when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
        if (entry != null) {
            when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
        }
        mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
    }
}