diff options
5 files changed, 179 insertions, 47 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 03149928249b..23fa40cfd5a6 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -377,6 +377,16 @@ flag { } flag { + name: "status_bar_swipe_over_chip" + namespace: "systemui" + description: "Allow users to swipe over the status bar chip to open the shade" + bug: "185897191" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "compose_bouncer" namespace: "systemui" description: "Use the new compose bouncer in SystemUI" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 8115c36c31e2..f1787088ab98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -36,6 +36,7 @@ import android.widget.LinearLayout; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dependency; +import com.android.systemui.Flags; import com.android.systemui.Gefingerpoken; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; @@ -217,8 +218,12 @@ public class PhoneStatusBarView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { - mTouchEventHandler.onInterceptTouchEvent(event); - return super.onInterceptTouchEvent(event); + if (Flags.statusBarSwipeOverChip()) { + return mTouchEventHandler.onInterceptTouchEvent(event); + } else { + mTouchEventHandler.onInterceptTouchEvent(event); + return super.onInterceptTouchEvent(event); + } } public void updateResources() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 5206e46a4580..a818c05b1666 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -23,9 +23,10 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver +import com.android.systemui.Flags import com.android.systemui.Gefingerpoken import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags +import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.ui.view.WindowRootView @@ -83,22 +84,25 @@ private constructor( statusContainer.setOnHoverListener( statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer) ) - statusContainer.setOnTouchListener(object : View.OnTouchListener { - override fun onTouch(v: View, event: MotionEvent): Boolean { - // We want to handle only mouse events here to avoid stealing finger touches from - // status bar which expands shade when swiped down on. We're using onTouchListener - // instead of onClickListener as the later will lead to isClickable being set to - // true and hence ALL touches always being intercepted. See [View.OnTouchEvent] - if (event.source == InputDevice.SOURCE_MOUSE) { - if (event.action == MotionEvent.ACTION_UP) { - v.performClick() - shadeController.animateExpandShade() + statusContainer.setOnTouchListener( + object : View.OnTouchListener { + override fun onTouch(v: View, event: MotionEvent): Boolean { + // We want to handle only mouse events here to avoid stealing finger touches + // from status bar which expands shade when swiped down on. See b/326097469. + // We're using onTouchListener instead of onClickListener as the later will lead + // to isClickable being set to true and hence ALL touches always being + // intercepted. See [View.OnTouchEvent] + if (event.source == InputDevice.SOURCE_MOUSE) { + if (event.action == MotionEvent.ACTION_UP) { + v.performClick() + shadeController.animateExpandShade() + } + return true } - return true + return false } - return false } - }) + ) progressProvider?.setReadyToHandleTransition(true) configurationController.addCallback(configurationListener) @@ -180,8 +184,12 @@ private constructor( inner class PhoneStatusBarViewTouchHandler : Gefingerpoken { override fun onInterceptTouchEvent(event: MotionEvent): Boolean { - onTouch(event) - return false + return if (Flags.statusBarSwipeOverChip()) { + shadeViewController.handleExternalInterceptTouch(event) + } else { + onTouch(event) + false + } } override fun onTouchEvent(event: MotionEvent): Boolean { @@ -280,7 +288,7 @@ private constructor( ) { fun create(view: PhoneStatusBarView): PhoneStatusBarViewController { val statusBarMoveFromCenterAnimationController = - if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) { + if (featureFlags.isEnabled(ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) { unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController() } else { null diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 25314f379e7c..5b4578153233 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -20,6 +20,8 @@ import android.app.StatusBarManager.WINDOW_STATE_HIDDEN import android.app.StatusBarManager.WINDOW_STATE_HIDING import android.app.StatusBarManager.WINDOW_STATE_SHOWING import android.app.StatusBarManager.WINDOW_STATUS_BAR +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.view.InputDevice import android.view.LayoutInflater import android.view.MotionEvent @@ -104,7 +106,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { val parent = FrameLayout(mContext) // add parent to keep layout params view = LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false) - as PhoneStatusBarView + as PhoneStatusBarView controller = createAndInitController(view) } } @@ -112,8 +114,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Test fun onViewAttachedAndDrawn_startListeningConfigurationControllerCallback() { val view = createViewMock() - val argumentCaptor = ArgumentCaptor.forClass( - ConfigurationController.ConfigurationListener::class.java) + val argumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) InstrumentationRegistry.getInstrumentation().runOnMainSync { controller = createAndInitController(view) } @@ -159,7 +161,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false) val returnVal = - view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) + view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)) assertThat(returnVal).isFalse() verify(shadeViewController, never()).handleExternalTouch(any()) } @@ -169,7 +171,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true) `when`(shadeViewController.isViewEnabled).thenReturn(false) val returnVal = - view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) + view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)) assertThat(returnVal).isTrue() verify(shadeViewController, never()).handleExternalTouch(any()) } @@ -178,7 +180,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { fun handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true) `when`(shadeViewController.isViewEnabled).thenReturn(false) - val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 2f, 0) view.onTouchEvent(event) @@ -208,6 +210,50 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { } @Test + @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOff_viewReturnsFalse() { + `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0) + + val returnVal = view.onInterceptTouchEvent(event) + + assertThat(returnVal).isFalse() + } + + @Test + @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOn_viewReturnsFalse() { + `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0) + + val returnVal = view.onInterceptTouchEvent(event) + + assertThat(returnVal).isFalse() + } + + @Test + @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOff_viewReturnsFalse() { + `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0) + + val returnVal = view.onInterceptTouchEvent(event) + + assertThat(returnVal).isFalse() + } + + @Test + @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOn_viewReturnsTrue() { + `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0) + + val returnVal = view.onInterceptTouchEvent(event) + + assertThat(returnVal).isTrue() + } + + @Test fun onTouch_windowHidden_centralSurfacesNotNotified() { val callback = getCommandQueueCallback() callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN) @@ -244,9 +290,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { controller = createAndInitController(view) } val statusContainer = view.requireViewById<View>(R.id.system_icons) - statusContainer.dispatchTouchEvent( - getActionUpEventFromSource(InputDevice.SOURCE_MOUSE) - ) + statusContainer.dispatchTouchEvent(getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)) verify(shadeControllerImpl).animateExpandShade() } @@ -257,9 +301,10 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { controller = createAndInitController(view) } val statusContainer = view.requireViewById<View>(R.id.system_icons) - val handled = statusContainer.dispatchTouchEvent( - getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN) - ) + val handled = + statusContainer.dispatchTouchEvent( + getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN) + ) assertThat(handled).isFalse() } @@ -295,21 +340,21 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController { return PhoneStatusBarViewController.Factory( - Optional.of(sysuiUnfoldComponent), - Optional.of(progressProvider), - featureFlags, - userChipViewModel, - centralSurfacesImpl, - statusBarWindowStateController, - shadeControllerImpl, - shadeViewController, - panelExpansionInteractor, - windowRootView, - shadeLogger, - viewUtil, - configurationController, - mStatusOverlayHoverListenerFactory - ) + Optional.of(sysuiUnfoldComponent), + Optional.of(progressProvider), + featureFlags, + userChipViewModel, + centralSurfacesImpl, + statusBarWindowStateController, + shadeControllerImpl, + shadeViewController, + panelExpansionInteractor, + windowRootView, + shadeLogger, + viewUtil, + configurationController, + mStatusOverlayHoverListenerFactory + ) .create(view) .also { it.init() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt index eae4f23c59d6..abc50bc09e55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone import android.content.res.Configuration import android.graphics.Insets import android.graphics.Rect +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import android.view.DisplayShape @@ -30,6 +32,7 @@ import android.view.View import android.view.WindowInsets import android.widget.FrameLayout import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP import com.android.systemui.Gefingerpoken import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.DarkIconDispatcher @@ -82,7 +85,8 @@ class PhoneStatusBarViewTest : SysuiTestCase() { } @Test - fun onInterceptTouchEvent_listenerNotified() { + @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_flagOff_listenerNotified() { val handler = TestTouchEventHandler() view.setTouchEventHandler(handler) @@ -93,6 +97,66 @@ class PhoneStatusBarViewTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_flagOn_listenerNotified() { + val handler = TestTouchEventHandler() + view.setTouchEventHandler(handler) + + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + view.onInterceptTouchEvent(event) + + assertThat(handler.lastInterceptEvent).isEqualTo(event) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_listenerReturnsFalse_flagOff_viewReturnsFalse() { + val handler = TestTouchEventHandler() + view.setTouchEventHandler(handler) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + + handler.handleTouchReturnValue = false + + assertThat(view.onInterceptTouchEvent(event)).isFalse() + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_listenerReturnsFalse_flagOn_viewReturnsFalse() { + val handler = TestTouchEventHandler() + view.setTouchEventHandler(handler) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + + handler.handleTouchReturnValue = false + + assertThat(view.onInterceptTouchEvent(event)).isFalse() + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_listenerReturnsTrue_flagOff_viewReturnsFalse() { + val handler = TestTouchEventHandler() + view.setTouchEventHandler(handler) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + + handler.handleTouchReturnValue = true + + assertThat(view.onInterceptTouchEvent(event)).isFalse() + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP) + fun onInterceptTouchEvent_listenerReturnsTrue_flagOn_viewReturnsTrue() { + val handler = TestTouchEventHandler() + view.setTouchEventHandler(handler) + val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + + handler.handleTouchReturnValue = true + + assertThat(view.onInterceptTouchEvent(event)).isTrue() + } + + @Test fun onTouchEvent_listenerReturnsTrue_viewReturnsTrue() { val handler = TestTouchEventHandler() view.setTouchEventHandler(handler) |