Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +13 −2 Original line number Diff line number Diff line Loading @@ -212,6 +212,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; Loading Loading @@ -515,7 +516,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)); Loading Loading @@ -652,12 +654,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); Loading Loading @@ -911,7 +922,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) { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +58 −0 Original line number Diff line number Diff line Loading @@ -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() { Loading packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt 0 → 100644 +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) } } packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +29 −0 Original line number Diff line number Diff line Loading @@ -365,6 +365,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; Loading Loading @@ -3098,6 +3099,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; Loading Loading @@ -3761,6 +3781,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 Loading Loading @@ -5077,6 +5098,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; Loading Loading @@ -5157,6 +5185,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mUpdateFlingOnLayout = false; mMotionAborted = false; mDownTime = mSystemClock.uptimeMillis(); mStatusBarLongPressDowntime = 0L; mTouchAboveFalsingThreshold = false; mCollapsedAndHeadsUpOnDown = isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp(); Loading packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +13 −2 Original line number Diff line number Diff line Loading @@ -212,6 +212,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; Loading Loading @@ -515,7 +516,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)); Loading Loading @@ -652,12 +654,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); Loading Loading @@ -911,7 +922,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) { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +58 −0 Original line number Diff line number Diff line Loading @@ -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() { Loading
packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt 0 → 100644 +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) } }
packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +29 −0 Original line number Diff line number Diff line Loading @@ -365,6 +365,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; Loading Loading @@ -3098,6 +3099,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; Loading Loading @@ -3761,6 +3781,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 Loading Loading @@ -5077,6 +5098,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; Loading Loading @@ -5157,6 +5185,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mUpdateFlingOnLayout = false; mMotionAborted = false; mDownTime = mSystemClock.uptimeMillis(); mStatusBarLongPressDowntime = 0L; mTouchAboveFalsingThreshold = false; mCollapsedAndHeadsUpOnDown = isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp(); Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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