From 03dd831d1f9edcfe56a60b200036e7ebfe1b8bef Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Wed, 2 Apr 2025 13:21:54 -0700 Subject: Disable LayoutTransitions for transient taskbar. Taskbar stashes once an app is launched from taskbar, or remains stashed if you launch something from All Apps. The layout transitions therefore are unlikely to be seen by the user. Disabling them means when the user unstashes, they will see the end result of the transition immediately. The problem with them being enabled for transient taskbar is we set icon alpha to 0 if Taskbar is stashed, which conflicts with the alpha layout transition animation. Flag: com.android.window.flags.enable_taskbar_recents_layout_transition Fix: 406809935 Test: Manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c0af778af1bf99e3536450c6ecc054d1d8d44953) Merged-In: Ibcfc04e5c9af104725a69c8cceccef49544d7bd3 Change-Id: Ibcfc04e5c9af104725a69c8cceccef49544d7bd3 --- quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java | 1 + 1 file changed, 1 insertion(+) 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()); -- cgit v1.2.3-59-g8ed1b From a88c057874b81a4bbdd72e27cd8cfaf2863afb98 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Wed, 2 Apr 2025 17:04:37 -0700 Subject: [Memory Leak] Fix leak of Launcher activity from recent view's input consumer Ensure we always clear the consumer, which has strong ref to RecentsView and launcher activity, once gesture is ended Fix: 407823225 Test: presubmit Flag: NONE - released code (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:222fb581bda7f16b8f44c41aae489a025507ef51) Merged-In: I90f2212247965697a4077890404bb577943efa37 Change-Id: I90f2212247965697a4077890404bb577943efa37 --- .../com/android/quickstep/AbsSwipeUpHandler.java | 4 + .../inputconsumers/OtherActivityInputConsumer.java | 2 + .../quickstep/util/CachedEventDispatcher.java | 5 + .../quickstep/util/CachedEventDispatcherTest.kt | 106 +++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 quickstep/tests/multivalentTests/src/com/android/quickstep/util/CachedEventDispatcherTest.kt 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 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 + @Captor private lateinit var motionEventCaptor: ArgumentCaptor + + 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 + } +} -- cgit v1.2.3-59-g8ed1b