diff options
| author | 2024-04-11 23:17:11 +0000 | |
|---|---|---|
| committer | 2024-04-11 23:17:11 +0000 | |
| commit | 764be7d29c047c7eb824492d1b146bb1fce2b45c (patch) | |
| tree | e38806afc6af0f2430d3a7f6722572078fb101b7 | |
| parent | e69174182e54515c8b354cf34c4f58c89df36d28 (diff) | |
| parent | ce3c21036468c4fa0945cc8dd6377e43cf65c4a8 (diff) | |
Merge changes Ied60cbab,I2769b0ad into main
* changes:
Only pilfer once in InputSession.
Refactor InputSession dependencies.
5 files changed, 244 insertions, 17 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index ef5e292f828f..c979d053617a 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -773,4 +773,14 @@ flag { metadata { purpose: PURPOSE_BUGFIX } -}
\ No newline at end of file +} + +flag { + name: "dream_input_session_pilfer_once" + namespace: "systemui" + description: "Pilfer at most once per input session" + bug: "324600132" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java index e1d03392044a..cddba04b5a7c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java @@ -16,7 +16,6 @@ package com.android.systemui.dreams.touch; -import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME; import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME; import android.os.Looper; @@ -24,7 +23,8 @@ import android.view.Choreographer; import android.view.GestureDetector; import android.view.MotionEvent; -import com.android.systemui.settings.DisplayTracker; +import com.android.systemui.Flags; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.shared.system.InputMonitorCompat; @@ -42,26 +42,34 @@ public class InputSession { private final InputChannelCompat.InputEventReceiver mInputEventReceiver; private final GestureDetector mGestureDetector; + // Pilfering is a destructive operation. Once pilfering starts, the all events will be captured + // by the associated monitor. We track whether we're pilfering since initiating pilfering + // requires reaching out to the InputManagerService, which can be a heavy operation. This is + // especially costly if this is happening on a continuous stream of motion events. + private boolean mPilfering; + /** * Default session constructor. - * @param sessionName The session name that will be applied to the underlying - * {@link InputMonitorCompat}. + * @param inputMonitor Input monitor to track input events. + * @param gestureDetector Gesture detector for detecting gestures. * @param inputEventListener A listener to receive input events. - * @param gestureListener A listener to receive gesture events. + * @param choreographer Choreographer to use with the input receiver. + * @param looper Looper to use with the input receiver * @param pilferOnGestureConsume Whether touch events should be pilfered after a gesture has * been consumed. */ @Inject - public InputSession(@Named(INPUT_SESSION_NAME) String sessionName, + public InputSession( + InputMonitorCompat inputMonitor, + GestureDetector gestureDetector, InputChannelCompat.InputEventListener inputEventListener, - GestureDetector.OnGestureListener gestureListener, - DisplayTracker displayTracker, + Choreographer choreographer, + @Main Looper looper, @Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) { - mInputMonitor = new InputMonitorCompat(sessionName, displayTracker.getDefaultDisplayId()); - mGestureDetector = new GestureDetector(gestureListener); + mInputMonitor = inputMonitor; + mGestureDetector = gestureDetector; - mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(), - Choreographer.getInstance(), + mInputEventReceiver = mInputMonitor.getInputReceiver(looper, choreographer, ev -> { // Process event. Since sometimes input may be a prerequisite for some // gesture logic, process input first. @@ -69,7 +77,9 @@ public class InputSession { if (ev instanceof MotionEvent && mGestureDetector.onTouchEvent((MotionEvent) ev) - && pilferOnGestureConsume) { + && pilferOnGestureConsume + && !(mPilfering && Flags.dreamInputSessionPilferOnce())) { + mPilfering = true; mInputMonitor.pilferPointers(); } }); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java index ad59a2e2b5c3..0b145211cd45 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java @@ -24,16 +24,18 @@ import android.view.GestureDetector; import com.android.systemui.dreams.touch.InputSession; import com.android.systemui.shared.system.InputChannelCompat; -import javax.inject.Named; - import dagger.BindsInstance; import dagger.Subcomponent; +import javax.inject.Named; + /** * {@link InputSessionComponent} generates {@link InputSession} with specific instances bound for * the session name and whether touches should be pilfered when consumed. */ -@Subcomponent +@Subcomponent( + modules = { InputSessionModule.class } +) public interface InputSessionComponent { /** * Generates {@link InputSessionComponent}. diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionModule.java new file mode 100644 index 000000000000..dfab666d5f59 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionModule.java @@ -0,0 +1,50 @@ +/* + * 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.dreams.touch.dagger; + +import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME; + +import android.view.GestureDetector; + +import com.android.systemui.settings.DisplayTracker; +import com.android.systemui.shared.system.InputMonitorCompat; + +import dagger.Module; +import dagger.Provides; + +import javax.inject.Named; + + +/** + * Module for providing dependencies to {@link com.android.systemui.dreams.touch.InputSession}. + */ +@Module +public interface InputSessionModule { + /** */ + @Provides + static InputMonitorCompat providesInputMonitorCompat(@Named(INPUT_SESSION_NAME) String name, + DisplayTracker displayTracker) { + return new InputMonitorCompat(name, displayTracker.getDefaultDisplayId()); + } + + /** */ + @Provides + static GestureDetector providesGestureDetector( + android.view.GestureDetector.OnGestureListener gestureListener) { + return new GestureDetector(gestureListener); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/InputSessionTest.java new file mode 100644 index 000000000000..8685384bb243 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/InputSessionTest.java @@ -0,0 +1,155 @@ +/* + * 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.dreams.touch; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.EnableFlags; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.Choreographer; +import android.view.GestureDetector; +import android.view.InputEvent; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.Flags; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.InputChannelCompat; +import com.android.systemui.shared.system.InputMonitorCompat; + +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; + +/** + * A test suite for exercising {@link InputSession}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper() +public class InputSessionTest extends SysuiTestCase { + @Mock + InputMonitorCompat mInputMonitor; + + @Mock + GestureDetector mGestureDetector; + + @Mock + InputChannelCompat.InputEventListener mInputEventListener; + + TestableLooper mLooper; + + @Mock + Choreographer mChoreographer; + + @Mock + InputChannelCompat.InputEventReceiver mInputEventReceiver; + + InputSession mSession; + + InputChannelCompat.InputEventListener mEventListener; + + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mLooper = TestableLooper.get(this); + } + + private void createSession(boolean pilfer) { + when(mInputMonitor.getInputReceiver(any(), any(), any())) + .thenReturn(mInputEventReceiver); + mSession = new InputSession(mInputMonitor, mGestureDetector, + mInputEventListener, mChoreographer, mLooper.getLooper(), pilfer); + final ArgumentCaptor<InputChannelCompat.InputEventListener> listenerCaptor = + ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); + verify(mInputMonitor).getInputReceiver(any(), any(), listenerCaptor.capture()); + mEventListener = listenerCaptor.getValue(); + } + + /** + * Ensures consumed motion events are pilfered when option is set. + */ + @Test + public void testPilferOnMotionEventGestureConsume() { + createSession(true); + final MotionEvent event = Mockito.mock(MotionEvent.class); + when(mGestureDetector.onTouchEvent(event)).thenReturn(true); + mEventListener.onInputEvent(event); + verify(mInputEventListener).onInputEvent(eq(event)); + verify(mInputMonitor).pilferPointers(); + } + + /** + * Ensures consumed motion events are not pilfered when option is not set. + */ + @Test + public void testNoPilferOnMotionEventGestureConsume() { + createSession(false); + final MotionEvent event = Mockito.mock(MotionEvent.class); + when(mGestureDetector.onTouchEvent(event)).thenReturn(true); + mEventListener.onInputEvent(event); + verify(mInputEventListener).onInputEvent(eq(event)); + verify(mInputMonitor, never()).pilferPointers(); + } + + /** + * Ensures input events are never pilfered. + */ + @Test + public void testNoPilferOnInputEvent() { + createSession(true); + final InputEvent event = Mockito.mock(InputEvent.class); + mEventListener.onInputEvent(event); + verify(mInputEventListener).onInputEvent(eq(event)); + verify(mInputMonitor, never()).pilferPointers(); + } + + @Test + @EnableFlags(Flags.FLAG_DREAM_INPUT_SESSION_PILFER_ONCE) + public void testPilferOnce() { + createSession(true); + final MotionEvent event = Mockito.mock(MotionEvent.class); + when(mGestureDetector.onTouchEvent(event)).thenReturn(true); + mEventListener.onInputEvent(event); + mEventListener.onInputEvent(event); + verify(mInputEventListener, times(2)).onInputEvent(eq(event)); + verify(mInputMonitor, times(1)).pilferPointers(); + } + + /** + * Ensures components are properly disposed. + */ + @Test + public void testDispose() { + createSession(true); + mSession.dispose(); + verify(mInputMonitor).dispose(); + verify(mInputEventReceiver).dispose(); + } +} |