summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java1
-rw-r--r--quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java4
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java2
-rw-r--r--quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java5
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/util/CachedEventDispatcherTest.kt106
5 files changed, 118 insertions, 0 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4b600eed15..ce38e666f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -1218,6 +1218,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
public void commitRunningAppsToUI() {
mModelCallbacks.commitRunningAppsToUI();
if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+ && !mActivity.isTransientTaskbar()
&& mTaskbarView.getLayoutTransition() == null) {
// Set up after the first commit so that the initial recents do not animate (janky).
mTaskbarView.setLayoutTransition(createLayoutTransitionForRunningApps());
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index f803709a46..8eb9c754f2 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -2348,6 +2348,10 @@ public abstract class AbsSwipeUpHandler<
}
}
+ /**
+ * The returned Consumer has strong ref to RecentsView and thus Launcher activity. Caller should
+ * ensure it clears the ref to returned consumer once gesture is ended.
+ */
public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 54cc391293..5a1e5b5330 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -521,6 +521,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mVelocityTracker = null;
}
mMotionPauseDetector.clear();
+ // Clear ref to recents view and launcher activity on action up or cancel to avoid leak
+ mRecentsViewDispatcher.clearConsumer();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
index 194c7d4f41..6b00a1d5c1 100644
--- a/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
+++ b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
@@ -63,6 +63,11 @@ public class CachedEventDispatcher {
mLastEvent = null;
}
+ /** Clear the consumer. */
+ public void clearConsumer() {
+ mConsumer = null;
+ }
+
public boolean hasConsumer() {
return mConsumer != null;
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/CachedEventDispatcherTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/CachedEventDispatcherTest.kt
new file mode 100644
index 0000000000..296171b866
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/CachedEventDispatcherTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 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.quickstep.util
+
+import android.os.SystemClock
+import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import java.util.function.Consumer
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidJUnit4::class)
+class CachedEventDispatcherTest {
+
+ private lateinit var underTest: CachedEventDispatcher
+
+ @Mock private lateinit var consumer: Consumer<MotionEvent>
+ @Captor private lateinit var motionEventCaptor: ArgumentCaptor<MotionEvent>
+
+ private lateinit var motionEvent: MotionEvent
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ motionEvent =
+ MotionEvent.obtain(
+ 0L,
+ SystemClock.elapsedRealtime(),
+ MotionEvent.ACTION_DOWN,
+ 0f,
+ 0f,
+ 0,
+ )
+ underTest = CachedEventDispatcher()
+ }
+
+ @Test
+ fun testInitialState() {
+ assertFalse(underTest.hasConsumer())
+ }
+
+ @Test
+ fun dispatchMotionEvents() {
+ underTest.setConsumer(consumer)
+
+ underTest.dispatchEvent(motionEvent)
+
+ assertTrue(underTest.hasConsumer())
+ verify(consumer).accept(motionEventCaptor.capture())
+ assertTrue(isMotionEventSame(motionEventCaptor.value, motionEvent))
+ }
+
+ @Test
+ fun dispatchMotionEvents_after_settingConsumer() {
+ underTest.dispatchEvent(motionEvent)
+
+ underTest.setConsumer(consumer)
+
+ verify(consumer).accept(motionEventCaptor.capture())
+ assertTrue(isMotionEventSame(motionEventCaptor.value, motionEvent))
+ }
+
+ @Test
+ fun clearConsumer_notDispatchToConsumer() {
+ underTest.setConsumer(consumer)
+ underTest.dispatchEvent(motionEvent)
+ reset(consumer)
+
+ underTest.clearConsumer()
+
+ assertFalse(underTest.hasConsumer())
+ underTest.dispatchEvent(motionEvent)
+ verifyNoMoreInteractions(consumer)
+ }
+
+ private fun isMotionEventSame(e1: MotionEvent, e2: MotionEvent): Boolean {
+ return e1.action == e2.action &&
+ e1.eventTime == e2.eventTime &&
+ e1.x == e2.x &&
+ e1.y == e2.y
+ }
+}