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

Commit a38e46ce authored by Justin Weir's avatar Justin Weir
Browse files

Long press Home status bar expands the shade

Bug: 371224114
Test: Added unit test
Test: Manually verified with go/shade-cujs
Flag: com.android.systemui.shade_expands_on_status_bar_long_press
Change-Id: Ie55e5a5cdfbbc62fc0ccdfadea90474bf3995b9c
parent c19a2266
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -211,6 +211,7 @@ import org.junit.Rule;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
@@ -511,7 +512,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
        when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
        when(mView.findViewById(R.id.keyguard_status_view))
                .thenReturn(mock(KeyguardStatusView.class));
        View rootView = mock(View.class);
        ViewGroup rootView = mock(ViewGroup.class);
        when(rootView.isVisibleToUser()).thenReturn(true);
        when(mView.getRootView()).thenReturn(rootView);
        when(rootView.findViewById(R.id.keyguard_status_view))
                .thenReturn(mock(KeyguardStatusView.class));
@@ -648,12 +650,21 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
            ((Runnable) invocation.getArgument(0)).run();
            return null;
        }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
        when(mNotificationShadeWindowController.getWindowRootView()).thenReturn(rootView);
        doAnswer(invocation -> {
            mLayoutChangeListener = invocation.getArgument(0);
            return null;
        }).when(mView).addOnLayoutChangeListener(any());

        when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                ViewTreeObserver.OnGlobalLayoutListener gll = invocation.getArgument(0);
                gll.onGlobalLayout();
                return null;
            }
        }).when(mViewTreeObserver).addOnGlobalLayoutListener(any());
        when(mView.getParent()).thenReturn(mViewParent);
        when(mQs.getHeader()).thenReturn(mQsHeader);
        when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
@@ -906,7 +917,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
    }

    protected boolean onTouchEvent(MotionEvent ev) {
        return mTouchHandler.onTouch(mView, ev);
        return mNotificationPanelViewController.handleExternalTouch(ev);
    }

    protected void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+58 −0
Original line number Diff line number Diff line
@@ -363,6 +363,64 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
        assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
    }

    @Test
    @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
    public void onStatusBarLongPress_shadeExpands() {
        long downTime = 42L;
        // Start touch session with down event
        onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
        // Status bar triggers long press expand
        mNotificationPanelViewController.onStatusBarLongPress(
                MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
        assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
        // Shade ignores the rest of the long press's touch session
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
                        0))).isFalse();

        // Start new touch session
        long downTime2 = downTime + 100L;
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
                        0))).isTrue();
        // Shade no longer ignoring touches
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
                        0))).isTrue();
    }

    @Test
    @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
    public void onStatusBarLongPress_qsExpands() {
        long downTime = 42L;
        // Start with shade already expanded
        mNotificationPanelViewController.setExpandedFraction(1F);

        // Start touch session with down event
        onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
        // Status bar triggers long press expand
        mNotificationPanelViewController.onStatusBarLongPress(
                MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
        assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
        // Shade expands to QS
        verify(mQsController, atLeastOnce()).flingQs(0F, ShadeViewController.FLING_EXPAND);
        // Shade ignores the rest of the long press's touch session
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
                        0))).isFalse();

        // Start new touch session
        long downTime2 = downTime + 100L;
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
                        0))).isTrue();
        // Shade no longer ignoring touches
        assertThat(onTouchEvent(
                MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
                        0))).isTrue();

    }

    @Test
    @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
    public void test_pulsing_onTouchEvent_noTracking() {
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.shade

import android.content.Context
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject

/** Accepts touch events, detects long press, and calls ShadeViewController#onStatusBarLongPress. */
@SysUISingleton
class LongPressGestureDetector
@Inject
constructor(context: Context, val shadeViewController: ShadeViewController) {
    val gestureDetector =
        GestureDetector(
            context,
            object : SimpleOnGestureListener() {
                override fun onLongPress(event: MotionEvent) {
                    shadeViewController.onStatusBarLongPress(event)
                }
            },
        )

    /** Accepts touch events to detect long presses. */
    fun handleTouch(ev: MotionEvent) {
        gestureDetector.onTouchEvent(ev)
    }
}
+29 −0
Original line number Diff line number Diff line
@@ -363,6 +363,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
    private final TouchHandler mTouchHandler = new TouchHandler();

    private long mDownTime;
    private long mStatusBarLongPressDowntime;
    private boolean mTouchSlopExceededBeforeDown;
    private float mOverExpansion;
    private CentralSurfaces mCentralSurfaces;
@@ -3087,6 +3088,25 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
        }
    }

    /** @deprecated Temporary a11y solution until dual shade launch b/371224114 */
    @Override
    @Deprecated
    public void onStatusBarLongPress(MotionEvent event) {
        mShadeLog.d("Status Bar was long pressed.");
        ShadeExpandsOnStatusBarLongPress.assertInNewMode();
        mStatusBarLongPressDowntime = event.getDownTime();
        if (isTracking()) {
            onTrackingStopped(true);
        }
        if (isExpanded() && !mQsController.getExpanded()) {
            mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
            expandToQs();
        } else {
            mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
            expandToNotifications();
        }
    }

    @Override
    public int getBarState() {
        return mBarState;
@@ -3750,6 +3770,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
    private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
        mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
        mTrackingPointer = -1;
        mStatusBarLongPressDowntime = 0L;
        mAmbientState.setSwipingUp(false);
        if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
                || Math.abs(y - mInitialExpandY) > mTouchSlop
@@ -5066,6 +5087,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
                }
                return true;
            }
            // This touch session has already resulted in shade expansion. Ignore everything else.
            if (ShadeExpandsOnStatusBarLongPress.isEnabled()
                    && event.getActionMasked() != MotionEvent.ACTION_DOWN
                    && event.getDownTime() == mStatusBarLongPressDowntime) {
                mShadeLog.d("Touch has same down time as Status Bar long press. Ignoring.");
                return false;
            }
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
                mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
                handled = true;
@@ -5146,6 +5174,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
                    mUpdateFlingOnLayout = false;
                    mMotionAborted = false;
                    mDownTime = mSystemClock.uptimeMillis();
                    mStatusBarLongPressDowntime = 0L;
                    mTouchAboveFalsingThreshold = false;
                    mCollapsedAndHeadsUpOnDown =
                            isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
+5 −3
Original line number Diff line number Diff line
@@ -98,6 +98,10 @@ interface ShadeViewController {
    /** Returns the ShadeHeadsUpTracker. */
    val shadeHeadsUpTracker: ShadeHeadsUpTracker

    @Deprecated("Temporary a11y solution until dual shade launch b/371224114")
    /** Notifies the shade that a status bar detected a long press gesture. */
    fun onStatusBarLongPress(event: MotionEvent)

    /** Returns the ShadeFoldAnimator. */
    @Deprecated("This interface is deprecated in Scene Container")
    val shadeFoldAnimator: ShadeFoldAnimator
@@ -179,9 +183,7 @@ interface ShadeViewStateProvider {
    /** Returns the expanded height of the panel view. */
    @Deprecated("deprecated by SceneContainerFlag.isEnabled") val panelViewExpandedHeight: Float

    /**
     * Returns true if heads up should be visible.
     */
    /** Returns true if heads up should be visible. */
    @Deprecated("deprecated by SceneContainerFlag.isEnabled.") fun shouldHeadsUpBeVisible(): Boolean

    /** Return the fraction of the shade that's expanded, when in lockscreen. */
Loading