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

Commit d01ed79c authored by Alex Florescu's avatar Alex Florescu
Browse files

Notifications and quicksettings scroll independently in split shade

- Don't send overscroll from notifications to quicksettings
- Allow notifications to be scrollable even when quicksettings expanded
- Don't reduce notification stack height based on QS expansion

Bug: 171917882
Test: manual + atest NotificationPanelViewTest
Change-Id: I7d89f6325881974ef714c7a6f9e7268cd8f0b675
parent 00a39566
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;

import static java.lang.annotation.RetentionPolicy.SOURCE;

@@ -83,6 +84,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -453,6 +455,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    private NotificationEntry mTopHeadsUpEntry;
    private long mNumHeadsUp;
    private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
    private final FeatureFlags mFeatureFlags;

    private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
            new ExpandableView.OnHeightChangedListener() {
@@ -492,8 +495,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            GroupMembershipManager groupMembershipManager,
            GroupExpansionManager groupExpansionManager,
            SysuiStatusBarStateController statusbarStateController,
            AmbientState ambientState
    ) {
            AmbientState ambientState,
            FeatureFlags featureFlags) {
        super(context, attrs, 0, 0);
        Resources res = getResources();
        mSectionsManager = notificationSectionsManager;
@@ -530,6 +533,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mGroupMembershipManager = groupMembershipManager;
        mGroupExpansionManager = groupExpansionManager;
        mStatusbarStateController = statusbarStateController;
        mFeatureFlags = featureFlags;
    }

    void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
@@ -1155,10 +1159,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
                if (stackStartPosition <= stackEndPosition) {
                    stackHeight = stackEndPosition;
                } else {
                    if (shouldUseSplitNotificationShade(mFeatureFlags, getResources())) {
                        // This prevents notifications from being collapsed when QS is expanded.
                        stackHeight = (int) height;
                    } else {
                        stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
                                stackEndPosition, mQsExpansionFraction);
                    }
                }
            } else {
                stackHeight = (int) height;
            }
+19 −9
Original line number Diff line number Diff line
@@ -196,7 +196,8 @@ public class NotificationPanelViewController extends PanelViewController {
            new MyOnHeadsUpChangedListener();
    private final HeightListener mHeightListener = new HeightListener();
    private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
    private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
    @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
            new StatusBarStateListener();
    private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
    private final BiometricUnlockController mBiometricUnlockController;
    private final NotificationPanelView mView;
@@ -1852,7 +1853,7 @@ public class NotificationPanelViewController extends PanelViewController {
        }
    }

    private void setQsExpanded(boolean expanded) {
    @VisibleForTesting void setQsExpanded(boolean expanded) {
        boolean changed = mQsExpanded != expanded;
        if (changed) {
            mQsExpanded = expanded;
@@ -1955,8 +1956,10 @@ public class NotificationPanelViewController extends PanelViewController {
    private void updateQsState() {
        mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
        mNotificationStackScrollLayoutController.setScrollingEnabled(
                mBarState != KEYGUARD && (!mQsExpanded
                        || mQsExpansionFromOverscroll));
                mBarState != KEYGUARD
                        && (!mQsExpanded
                            || mQsExpansionFromOverscroll
                            || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)));

        if (mKeyguardUserSwitcherController != null && mQsExpanded
                && !mStackScrollerOverscrolling) {
@@ -2236,13 +2239,16 @@ public class NotificationPanelViewController extends PanelViewController {

    @Override
    protected boolean canCollapsePanelOnTouch() {
        if (!isInSettings()) {
            return mBarState == KEYGUARD
                    || mIsPanelCollapseOnQQS
                    || mNotificationStackScrollLayoutController.isScrolledToBottom();
        } else {
        if (!isInSettings() && mBarState == KEYGUARD) {
            return true;
        }

        if (mNotificationStackScrollLayoutController.isScrolledToBottom()) {
            return true;
        }

        return !Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)
                && (isInSettings() || mIsPanelCollapseOnQQS);
    }

    @Override
@@ -3615,6 +3621,10 @@ public class NotificationPanelViewController extends PanelViewController {
            NotificationStackScrollLayout.OnOverscrollTopChangedListener {
        @Override
        public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
            // When in split shade, overscroll shouldn't carry through to QS
            if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
                return;
            }
            cancelQsAnimation();
            if (!mQsExpansionEnabled) {
                amount = 0f;
+31 −4
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;

import static com.google.common.truth.Truth.assertWithMessage;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;

@@ -49,6 +51,7 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -101,6 +104,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
    @Mock private SysuiStatusBarStateController mStatusBarStateController;
    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
    @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
    @Mock private FeatureFlags mFeatureFlags;

    @Before
    @UiThreadTest
@@ -139,8 +143,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
                mGroupMembershipManger,
                mGroupExpansionManager,
                mStatusBarStateController,
                mAmbientState
        );
                mAmbientState,
                mFeatureFlags);
        mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
                mNotificationSwipeHelper);
        mStackScroller = spy(mStackScrollerInternal);
@@ -205,8 +209,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
    @Test
    @UiThreadTest
    public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
        final float expectedHeight[] = {0f};
        final float expectedAppear[] = {0f};
        final float[] expectedHeight = {0f};
        final float[] expectedAppear = {0f};

        mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
            Assert.assertEquals(expectedHeight[0], height, 0);
@@ -221,6 +225,29 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
        mStackScroller.setExpandedHeight(expectedHeight[0]);
    }

    @Test
    @UiThreadTest
    public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
        final int[] expectedStackHeight = {0};

        mStackScroller.addOnExpandedHeightChangedListener((expandedHeight, appear) -> {
            assertWithMessage("Given shade enabled: %s",
                    mFeatureFlags.isTwoColumnNotificationShadeEnabled())
                    .that(mStackScroller.getHeight())
                    .isEqualTo(expectedStackHeight[0]);
        });

        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
        expectedStackHeight[0] = 0;
        mStackScroller.setExpandedHeight(100f);

        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
        expectedStackHeight[0] = 100;
        mStackScroller.setExpandedHeight(100f);
    }


    @Test
    public void manageNotifications_visible() {
        FooterView view = mock(FooterView.class);
+52 −10
Original line number Diff line number Diff line
@@ -18,13 +18,15 @@ package com.android.systemui.statusbar.phone;

import static android.content.res.Configuration.ORIENTATION_PORTRAIT;

import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
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.spy;
@@ -45,6 +47,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;

@@ -53,6 +56,7 @@ import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
@@ -100,7 +104,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -116,8 +119,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
    @Mock
    private StatusBar mStatusBar;
    @Mock
    private SysuiStatusBarStateController mStatusBarStateController;
    @Mock
    private NotificationStackScrollLayout mNotificationStackScrollLayout;
    @Mock
    private KeyguardBottomAreaView mKeyguardBottomArea;
@@ -227,7 +228,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
    private AmbientState mAmbientState;
    @Mock
    private UserManager mUserManager;
    @Mock
    private UiEventLogger mUiEventLogger;

    private SysuiStatusBarStateController mStatusBarStateController;
    private NotificationPanelViewController mNotificationPanelViewController;
    private View.AccessibilityDelegate mAccessibiltyDelegate;
    private NotificationsQuickSettingsContainer mNotificationContainerParent;
@@ -235,6 +239,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger);

        when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
        when(mHeadsUpCallback.getContext()).thenReturn(mContext);
        when(mView.getResources()).thenReturn(mResources);
@@ -258,6 +264,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
        when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
        when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
        when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
        when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
        when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
        when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
        when(mView.findViewById(R.id.keyguard_status_view))
@@ -336,17 +343,19 @@ public class NotificationPanelViewTest extends SysuiTestCase {
                ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
        verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
        mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue();
        mNotificationPanelViewController.mStatusBarStateController
                .addCallback(mNotificationPanelViewController.mStatusBarStateListener);
        mNotificationPanelViewController
                .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
    }

    @Test
    public void testSetDozing_notifiesNsslAndStateController() {
        mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */,
        mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */,
                null /* touch */);
        InOrder inOrder = inOrder(
                mNotificationStackScrollLayoutController, mStatusBarStateController);
        inOrder.verify(mNotificationStackScrollLayoutController)
                .setDozing(eq(true), eq(true), eq(null));
        inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
        verify(mNotificationStackScrollLayoutController)
                .setDozing(eq(true), eq(false), eq(null));
        assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f);
    }

    @Test
@@ -489,6 +498,39 @@ public class NotificationPanelViewTest extends SysuiTestCase {
        assertThat(stackScrollerLayout.startToStart).isEqualTo(R.id.qs_edge_guideline);
    }

    @Test
    public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
        mStatusBarStateController.setState(KEYGUARD);

        assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
    }

    @Test
    public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() {
        mStatusBarStateController.setState(SHADE);
        when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true);

        assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
    }

    @Test
    public void testCanCollapsePanelOnTouch_trueWhenInSettings() {
        mStatusBarStateController.setState(SHADE);
        mNotificationPanelViewController.setQsExpanded(true);

        assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
    }

    @Test
    public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
        mStatusBarStateController.setState(SHADE);
        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
        mNotificationPanelViewController.setQsExpanded(true);

        assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
    }

    private View newViewWithId(int id) {
        View view = new View(mContext);
        view.setId(id);