diff options
| author | 2023-03-16 21:00:21 -0700 | |
|---|---|---|
| committer | 2023-03-31 07:05:54 +0000 | |
| commit | 76d6afe9dcb504499be4ffc22e434f3fb7e62db9 (patch) | |
| tree | 26a8ab8a1fb6ff8fe7e25257d26334da79d3ad97 | |
| parent | f296aa6cea59cccae0303b3047426f11763654aa (diff) | |
Allow swiping down notification shade over dream.
This changelist enables swiping down the notification shade over the
dream. NotificationShadeTouchHandler tracks swipes that originate from
the status bar area of the dream and redirects these motion events to
the NotificationPanelViewController.
Test: atest NotificationShadeTouchHandlerTest
Bug: 267565290
Change-Id: I72d54cf33601dacc0a2a6adfd2b7639615061c09
Merged-In: I72d54cf33601dacc0a2a6adfd2b7639615061c09
4 files changed, 271 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java new file mode 100644 index 000000000000..58b70b02e84f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 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.dreams.touch; + +import static com.android.systemui.dreams.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT; + +import android.graphics.Rect; +import android.graphics.Region; +import android.view.GestureDetector; +import android.view.MotionEvent; + +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.statusbar.phone.CentralSurfaces; + +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream + * to bring down the shade. + */ +public class ShadeTouchHandler implements DreamTouchHandler { + private final Optional<CentralSurfaces> mSurfaces; + private final int mInitiationHeight; + + @Inject + ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces, + @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) { + mSurfaces = centralSurfaces; + mInitiationHeight = initiationHeight; + } + + @Override + public void onSessionStart(TouchSession session) { + if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) { + session.pop(); + return; + } + + session.registerInputListener(ev -> { + final NotificationPanelViewController viewController = + mSurfaces.map(CentralSurfaces::getNotificationPanelViewController).orElse(null); + + if (viewController != null) { + viewController.handleExternalTouch((MotionEvent) ev); + } + + if (ev instanceof MotionEvent) { + if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) { + session.pop(); + } + } + }); + + session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + return true; + } + }); + } + + @Override + public void getTouchInitiationRegion(Rect bounds, Region region) { + final Rect outBounds = new Rect(bounds); + outBounds.inset(0, 0, 0, outBounds.height() - mInitiationHeight); + region.op(outBounds, Region.Op.UNION); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java index 7338ecba8cf3..3facc4b603b2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java @@ -24,6 +24,7 @@ import dagger.Module; @Module(includes = { BouncerSwipeModule.class, HideComplicationModule.class, + ShadeModule.class, }, subcomponents = { InputSessionComponent.class, }) diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java new file mode 100644 index 000000000000..4ecc4a7ca3f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 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.dreams.touch.dagger; + +import android.content.res.Resources; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.touch.DreamTouchHandler; +import com.android.systemui.dreams.touch.ShadeTouchHandler; + +import javax.inject.Named; + +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoSet; + +/** + * Dependencies for swipe down to notification over dream. + */ +@Module +public class ShadeModule { + /** + * The height, defined in pixels, of the gesture initiation region at the top of the screen for + * swiping down notifications. + */ + public static final String NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT = + "notification_shade_gesture_initiation_height"; + + /** + * Provides {@link ShadeTouchHandler} to handle notification swipe down over dream. + */ + @Provides + @IntoSet + public static DreamTouchHandler providesNotificationShadeTouchHandler( + ShadeTouchHandler touchHandler) { + return touchHandler; + } + + /** + * Provides the height of the gesture area for notification swipe down. + */ + @Provides + @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) + public static int providesNotificationShadeGestureRegionHeight(@Main Resources resources) { + return resources.getDimensionPixelSize(R.dimen.dream_overlay_status_bar_height); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java new file mode 100644 index 000000000000..5704ef3f37db --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 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.dreams.touch; + + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.view.GestureDetector; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shared.system.InputChannelCompat; +import com.android.systemui.statusbar.phone.CentralSurfaces; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ShadeTouchHandlerTest extends SysuiTestCase { + @Mock + CentralSurfaces mCentralSurfaces; + + @Mock + NotificationPanelViewController mNotificationPanelViewController; + + @Mock + DreamTouchHandler.TouchSession mTouchSession; + + ShadeTouchHandler mTouchHandler; + + private static final int TOUCH_HEIGHT = 20; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), + TOUCH_HEIGHT); + when(mCentralSurfaces.getNotificationPanelViewController()) + .thenReturn(mNotificationPanelViewController); + } + + /** + * Verify that touches aren't handled when the bouncer is showing. + */ + @Test + public void testInactiveOnBouncer() { + when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).pop(); + } + + /** + * Make sure {@link ShadeTouchHandler} + */ + @Test + public void testTouchPilferingOnScroll() { + final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class); + final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class); + + final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture()); + + assertThat(gestureListenerArgumentCaptor.getValue() + .onScroll(motionEvent1, motionEvent2, 1, 1)) + .isTrue(); + } + + /** + * Ensure touches are propagated to the {@link NotificationPanelViewController}. + */ + @Test + public void testEventPropagation() { + final MotionEvent motionEvent = Mockito.mock(MotionEvent.class); + + final ArgumentCaptor<InputChannelCompat.InputEventListener> + inputEventListenerArgumentCaptor = + ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); + + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture()); + inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent); + verify(mNotificationPanelViewController).handleExternalTouch(motionEvent); + } + +} |