diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml index a74a17b8dc..04b04dde75 100644 --- a/quickstep/res/values-en-rGB/strings.xml +++ b/quickstep/res/values-en-rGB/strings.xml @@ -139,8 +139,7 @@ "Always show Taskbar" "Change navigation mode" "Taskbar divider" - - + "Taskbar overflow" "Move to top/left" "Move to bottom/right" "{count,plural, =1{more app}other{more apps}}" diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 065646ad39..43b1ae04d5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -24,6 +24,7 @@ import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_FOR_BUBBLES; +import static com.android.launcher3.taskbar.TaskbarStashController.UNLOCK_TRANSITION_MEMOIZATION_MS; import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_ALPHA_DURATION_MS; import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS; @@ -167,7 +168,12 @@ public class TaskbarLauncherStateController { private boolean mSkipNextRecentsAnimEnd; // Time when FLAG_TASKBAR_HIDDEN was last cleared, SystemClock.elapsedRealtime (milliseconds). - private long mLastUnlockTimeMs = 0; + private long mLastRemoveTaskbarHiddenTimeMs = 0; + /** + * Time when FLAG_DEVICE_LOCKED was last cleared, plus + * {@link TaskbarStashController#UNLOCK_TRANSITION_MEMOIZATION_MS} + */ + private long mLastUnlockTransitionTimeout; private @Nullable TaskBarRecentsAnimationListener mTaskBarRecentsAnimationListener; @@ -535,7 +541,7 @@ public class TaskbarLauncherStateController { if (hasAnyFlag(changedFlags, FLAG_TASKBAR_HIDDEN) && !hasAnyFlag(FLAG_TASKBAR_HIDDEN)) { // Take note of the current time, as the taskbar is made visible again. - mLastUnlockTimeMs = SystemClock.elapsedRealtime(); + mLastRemoveTaskbarHiddenTimeMs = SystemClock.elapsedRealtime(); } boolean isHidden = hasAnyFlag(FLAG_TASKBAR_HIDDEN); @@ -561,7 +567,8 @@ public class TaskbarLauncherStateController { // with a fingerprint reader. This should only be done when the device was woken // up via fingerprint reader, however since this information is currently not // available, opting to always delay the fade-in a bit. - long durationSinceLastUnlockMs = SystemClock.elapsedRealtime() - mLastUnlockTimeMs; + long durationSinceLastUnlockMs = SystemClock.elapsedRealtime() + - mLastRemoveTaskbarHiddenTimeMs; taskbarVisibility.setStartDelay( Math.max(0, TASKBAR_SHOW_DELAY_MS - durationSinceLastUnlockMs)); } @@ -631,6 +638,15 @@ public class TaskbarLauncherStateController { boolean isUnlockTransition = hasAnyFlag(changedFlags, FLAG_DEVICE_LOCKED) && !hasAnyFlag(FLAG_DEVICE_LOCKED); if (isUnlockTransition) { + // the launcher might not be resumed at the time the device is considered + // unlocked (when the keyguard goes away), but possibly shortly afterwards. + // To play the unlock transition at the time the unstash animation actually happens, + // this memoizes the state transition for UNLOCK_TRANSITION_MEMOIZATION_MS. + mLastUnlockTransitionTimeout = + SystemClock.elapsedRealtime() + UNLOCK_TRANSITION_MEMOIZATION_MS; + } + boolean isInUnlockTimeout = SystemClock.elapsedRealtime() < mLastUnlockTransitionTimeout; + if (isUnlockTransition || isInUnlockTimeout) { // When transitioning to unlocked, ensure the hotseat is fully visible from the // beginning. The hotseat itself is animated by LauncherUnlockAnimationController. mIconAlignment.cancelAnimation(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 6222e53a5f..8991965f61 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -191,7 +191,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // Duration for which an unlock event is considered "current", as other events are received // asynchronously. - private static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200; + public static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200; /** * The default stash animation, morphing the taskbar into the navbar. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index b495e7d80e..e5220359d1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -329,7 +329,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar */ public void setRecentsButtonDisabled(boolean isDisabled) { // TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha. - mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1); + mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).animateToValue(isDisabled ? 0 : 1) + .start(); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt index b8c1c7f1d7..aa2555e865 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt @@ -39,4 +39,13 @@ interface BubbleBarFlyoutPositioner { /** The color of the flyout when collapsed. */ val collapsedColor: Int + + /** The elevation of the flyout when collapsed. */ + val collapsedElevation: Float + + /** + * The distance the flyout must pass from its collapsed position until it can start revealing + * the triangle. + */ + val distanceToRevealTriangle: Float } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt index 6723f57dd5..8d84ddf8ab 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt @@ -40,8 +40,6 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly ConstraintLayout(context) { private companion object { - // the minimum progress of the expansion animation before the triangle is made visible. - const val MIN_EXPANSION_PROGRESS_FOR_TRIANGLE = 0.1f // the minimum progress of the expansion animation before the content starts fading in. const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f } @@ -92,6 +90,11 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_max_width) } + private val flyoutElevation by + lazy(LazyThreadSafetyMode.NONE) { + context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat() + } + /** The bounds of the background rect. */ private val backgroundRect = RectF() private val cornerRadius: Float @@ -108,6 +111,10 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly private var collapsedCornerRadius = 0f /** The color of the flyout when collapsed. */ private var collapsedColor = 0 + /** The elevation of the flyout when collapsed. */ + private var collapsedElevation = 0f + /** The minimum progress of the expansion animation before the triangle is made visible. */ + private var minExpansionProgressForTriangle = 0f /** The corner radius of the background according to the progress of the animation. */ private val currentCornerRadius @@ -141,8 +148,7 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly val padding = context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding) // add extra padding to the bottom of the view to include the triangle setPadding(padding, padding, padding, padding + triangleHeight - triangleOverlap) - translationZ = - context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat() + translationZ = flyoutElevation RoundedArrowDrawable.addDownPointingRoundedTriangleToPath( triangleWidth.toFloat(), @@ -182,6 +188,12 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly collapsedSize = positioner.collapsedSize collapsedCornerRadius = collapsedSize / 2 collapsedColor = positioner.collapsedColor + collapsedElevation = positioner.collapsedElevation + + // calculate the expansion progress required before we start showing the triangle as part of + // the expansion animation + minExpansionProgressForTriangle = + positioner.distanceToRevealTriangle / tyToCollapsedPosition // post the request to start the expand animation to the looper so the view can measure // itself @@ -240,6 +252,9 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly message.alpha = alpha avatar.alpha = alpha + translationZ = + collapsedElevation + (flyoutElevation - collapsedElevation) * expansionProgress + invalidate() } @@ -275,7 +290,7 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly currentCornerRadius, backgroundPaint, ) - if (expansionProgress >= MIN_EXPANSION_PROGRESS_FOR_TRIANGLE) { + if (expansionProgress >= minExpansionProgressForTriangle) { drawTriangle(canvas) } canvas.restore() @@ -291,9 +306,20 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly } else { width - currentCornerRadius - triangleWidth } - // instead of scaling the triangle, increasingly reveal it from the background, starting - // with half the size. this has the effect of the triangle scaling. - val triangleY = height - triangleHeight - 0.5f * triangleHeight * (1 - expansionProgress) + // instead of scaling the triangle, increasingly reveal it from the background. this has the + // effect of the triangle scaling. + + // the translation y of the triangle before we start revealing it. align its bottom with the + // bottom of the rect + val triangleYCollapsed = height - triangleHeight - (triangleHeight - triangleOverlap) + // the translation y of the triangle when it's fully revealed + val triangleYExpanded = height - triangleHeight + val interpolatedExpansion = + ((expansionProgress - minExpansionProgressForTriangle) / + (1 - minExpansionProgressForTriangle)) + .coerceIn(0f, 1f) + val triangleY = + triangleYCollapsed + (triangleYExpanded - triangleYCollapsed) * interpolatedExpansion canvas.translate(triangleX, triangleY) canvas.drawPath(triangle, backgroundPaint) triangleOutline.setPath(triangle) @@ -309,7 +335,7 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly currentCornerRadius, Path.Direction.CW, ) - if (expansionProgress >= MIN_EXPANSION_PROGRESS_FOR_TRIANGLE) { + if (expansionProgress >= minExpansionProgressForTriangle) { path.addPath(triangleOutline.mPath) } outline.setPath(path) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 9ad6b30352..dc7ed2459e 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1149,12 +1149,18 @@ public abstract class AbsSwipeUpHandler< if (endTarget != NEW_TASK) { InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_QUICK_SWITCH); + } else { + InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH); } if (endTarget != HOME) { InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME); + } else { + InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME); } if (endTarget != RECENTS) { InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS); + } else { + InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS); } switch (endTarget) { diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 5028da4f6a..9510a059d0 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -52,7 +52,6 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.content.res.AppCompatResources; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; @@ -610,8 +609,8 @@ abstract class TutorialController implements BackGestureAttemptCallback, private void updateDrawables() { if (mContext != null) { - mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable( - mContext, getMockWallpaperResId())); + mTutorialFragment.getRootView() + .setBackground(mContext.getDrawable(getMockWallpaperResId())); mTutorialFragment.updateFeedbackAnimation(); mFakeLauncherView.setBackgroundColor(getFakeLauncherColor()); updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId()); @@ -619,9 +618,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, mFakeTaskView.animate().alpha(1).setListener( AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel())); mFakePreviousTaskView.setFakeTaskViewFillColor(getMockPreviousAppTaskThumbnailColor()); - mFakeIconView.setBackground(AppCompatResources.getDrawable( - mContext, getMockAppIconResId())); - + mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId())); mExitingAppView.setBackgroundColor(getExitingAppColor()); mFakeTaskView.setBackgroundColor(getFakeTaskViewColor()); updateHotseatChildViewColor(mHotseatIconView); diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java index ae0e725aad..f1fc1797c4 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java @@ -22,8 +22,6 @@ import android.util.Log; import android.widget.ImageView; import android.widget.LinearLayout; -import androidx.appcompat.content.res.AppCompatResources; - import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.GraphicsUtils; @@ -91,9 +89,8 @@ public class TutorialStepIndicator extends LinearLayout { int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor( getContext(), android.R.attr.textColorSecondaryInverse); for (int i = 0; i < mTotalSteps; i++) { - Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable( - getContext(), R.drawable.tutorial_step_indicator_pill); - + Drawable pageIndicatorPillDrawable = + getContext().getDrawable(R.drawable.tutorial_step_indicator_pill); if (i >= getChildCount()) { ImageView pageIndicatorPill = new ImageView(getContext()); pageIndicatorPill.setImageDrawable(pageIndicatorPillDrawable); diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt index 68690d2f43..c3b072deac 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt @@ -113,6 +113,12 @@ class RecentsViewUtils { it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden) } + /** Returns if any small tasks are fully visible */ + fun isAnySmallTaskFullyVisible( + taskViews: Iterable, + isTaskViewFullyVisible: (TaskView) -> Boolean, + ): Boolean = taskViews.any { !it.isLargeTile && isTaskViewFullyVisible(it) } + /** Returns the current list of [TaskView] children. */ fun getTaskViews(taskViewCount: Int, requireTaskViewAt: (Int) -> TaskView): Iterable = (0 until taskViewCount).map(requireTaskViewAt) diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index 3449cf2034..f708f4bb39 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -238,7 +238,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC taskViewHeight, ) val snapshotViewSize = - if (isPrimaryTaskSplitting) primarySnapshotViewSize else secondarySnapshotViewSize + if (isPrimaryTaskSplitting) secondarySnapshotViewSize else primarySnapshotViewSize if (deviceProfile.isLeftRightSplit) { // Center view first so scaling happens uniformly, alternatively we can move pivotX to 0 val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 36d8f45856..82f08e58e5 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1706,10 +1706,11 @@ public abstract class RecentsView< return; } TaskView taskView = getTaskViewAt(mNextPage); - // Snap to fully visible focused task and clear all button. boolean shouldSnapToLargeTask = taskView != null && taskView.isLargeTile() - && isTaskViewFullyVisible(taskView); + && !mUtils.isAnySmallTaskFullyVisible(getTaskViews(), + this::isTaskViewFullyVisible); boolean shouldSnapToClearAll = mNextPage == indexOfChild(mClearAllButton); + // Snap to large tile when grid tasks aren't fully visible or the clear all button. if (!shouldSnapToLargeTask && !shouldSnapToClearAll) { return; } @@ -3611,10 +3612,10 @@ public abstract class RecentsView< float dismissedTaskWidth = 0; float nextFocusedTaskWidth = 0; - // Non-grid specific properties. int[] oldScroll = new int[count]; int[] newScroll = new int[count]; int scrollDiffPerPage = 0; + // Non-grid specific properties. boolean needsCurveUpdates = false; if (showAsGrid) { @@ -3644,13 +3645,13 @@ public abstract class RecentsView< } } } - } else { - getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); - getPageScrolls(newScroll, false, - v -> v.getVisibility() != GONE && v != dismissedTaskView); - if (count > 1) { - scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]); - } + } + + getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); + getPageScrolls(newScroll, false, + v -> v.getVisibility() != GONE && v != dismissedTaskView); + if (count > 1) { + scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]); } float dismissTranslationInterpolationEnd = 1; @@ -3802,7 +3803,9 @@ public abstract class RecentsView< addDismissedTaskAnimations(dismissedTaskView, duration, anim); } } - } else if (!showAsGrid) { + } else if (!showAsGrid || (enableLargeDesktopWindowingTile() + && dismissedTaskView.isLargeTile() + && nextFocusedTaskView == null)) { int offset = getOffsetToDismissedTask(scrollDiffPerPage, dismissedIndex, taskCount); int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { @@ -3814,18 +3817,16 @@ public abstract class RecentsView< splitTimings); needsCurveUpdates = true; } - } else if (child instanceof TaskView) { - TaskView taskView = (TaskView) child; + } else if (child instanceof TaskView taskView) { if (isFocusedTaskDismissed) { if (nextFocusedTaskView != null && !isSameGridRow(taskView, nextFocusedTaskView)) { continue; } - } else { - if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) { - continue; - } + } else if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) { + continue; } + // Animate task with index >= dismissed index and in the same row as the // dismissed index or next focused index. Offset successive task dismissal // durations for a staggered effect. @@ -4136,15 +4137,18 @@ public abstract class RecentsView< * - Dragging an adjacent page on the left side (right side for RTL) */ private int getOffsetToDismissedTask(int scrollDiffPerPage, int dismissedIndex, int taskCount) { + // When mCurrentPage is ClearAllButton, use the last TaskView instead to calculate + // offset. + int currentPage = mCurrentPage == taskCount ? taskCount - 1 : mCurrentPage; int offset = mIsRtl ? scrollDiffPerPage : 0; - if (mCurrentPage == dismissedIndex) { + if (currentPage == dismissedIndex) { int lastPage = taskCount - 1; - if (mCurrentPage == lastPage) { + if (currentPage == lastPage) { offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage; } } else { // Dismissing an adjacent page. - int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR) + int negativeAdjacent = currentPage - 1; // (Right in RTL, left in LTR) if (dismissedIndex == negativeAdjacent) { offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage; } diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt index 50e2dcf7b4..6aba6a3c78 100644 --- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt +++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt @@ -224,7 +224,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutMessage( senderAvatar = ColorDrawable(Color.RED), senderName = "sender", - message = "collapsed 90% on left", + message = "expanded 90% on left", isGroupChat = true, ) ) {} @@ -249,7 +249,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutMessage( senderAvatar = ColorDrawable(Color.RED), senderName = "sender", - message = "collapsed 80% on right", + message = "expanded 80% on right", isGroupChat = true, ) ) {} @@ -265,5 +265,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { override val targetTy = 0f override val collapsedSize = 30f override val collapsedColor = Color.BLUE + override val collapsedElevation = 1f + override val distanceToRevealTriangle = 10f } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt index 0f02444b30..d857ae5579 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt @@ -55,6 +55,8 @@ class BubbleBarFlyoutControllerTest { override val distanceToCollapsedPosition = PointF(100f, 200f) override val collapsedSize = 30f override val collapsedColor = Color.BLUE + override val collapsedElevation = 1f + override val distanceToRevealTriangle = 50f } flyoutController = BubbleBarFlyoutController(flyoutContainer, positioner) } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt index c6a50d1d0f..412bdeb8ac 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt @@ -240,7 +240,7 @@ class TransientBubbleStashControllerTest { mTransientBubbleStashController.isStashed = false whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false) - // When unstash + // When stash getInstrumentation().runOnMainSync { mTransientBubbleStashController.updateStashedAndExpandedState( stash = true, diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index aee49beaca..f7b04a31fd 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -31,8 +31,7 @@ "Split screen" "App info for %1$s" "Usage settings for %1$s" - - + "New window" "Save app pair" "%1$s | %2$s" "This app pair isn\'t supported on this device" diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index e215cabcd7..6a401215f1 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -66,6 +66,7 @@ import com.android.launcher3.BuildConfig; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Flags; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedPropertySetter; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.icons.BitmapInfo; @@ -699,7 +700,9 @@ public class PrivateProfileManager extends UserProfileManager { mAllApps.mAH.get(MAIN).mRecyclerView.removeItemDecoration( mPrivateAppsSectionDecorator); // Call onAppsUpdated() because it may be canceled when this animation occurs. - mAllApps.getPersonalAppList().onAppsUpdated(); + if (!Utilities.isRunningInTestHarness()) { + mAllApps.getPersonalAppList().onAppsUpdated(); + } if (isPrivateSpaceHidden()) { // TODO (b/325455879): Figure out if we can avoid this. getMainRecyclerView().getAdapter().notifyDataSetChanged(); diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java index 0a50e8bc9e..9f6b40b075 100644 --- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java +++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java @@ -18,6 +18,7 @@ package com.android.launcher3.dagger; import android.content.Context; +import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.util.DaggerSingletonTracker; import dagger.BindsInstance; @@ -32,6 +33,8 @@ import dagger.BindsInstance; */ public interface LauncherBaseAppComponent { DaggerSingletonTracker getDaggerSingletonTracker(); + InstallSessionHelper getInstallSessionHelper(); + /** Builder for LauncherBaseAppComponent. */ interface Builder { @BindsInstance Builder appContext(@ApplicationContext Context context); diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index 124907f181..f36f595545 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -31,12 +31,17 @@ import androidx.annotation.WorkerThread; import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.SessionCommitReceiver; +import com.android.launcher3.dagger.ApplicationContext; +import com.android.launcher3.dagger.LauncherAppSingleton; +import com.android.launcher3.dagger.LauncherBaseAppComponent; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.util.ApplicationInfoWrapper; +import com.android.launcher3.util.DaggerSingletonObject; +import com.android.launcher3.util.DaggerSingletonTracker; +import com.android.launcher3.util.ExecutorUtil; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; -import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SafeCloseable; @@ -47,10 +52,13 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; +import javax.inject.Inject; + /** * Utility class to tracking install sessions */ @SuppressWarnings("NewApi") +@LauncherAppSingleton public class InstallSessionHelper implements SafeCloseable { @NonNull @@ -64,8 +72,8 @@ public class InstallSessionHelper implements SafeCloseable { private static final boolean DEBUG = false; @NonNull - public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(InstallSessionHelper::new); + public static final DaggerSingletonObject INSTANCE = + new DaggerSingletonObject<>(LauncherBaseAppComponent::getInstallSessionHelper); @Nullable private final LauncherApps mLauncherApps; @@ -82,10 +90,13 @@ public class InstallSessionHelper implements SafeCloseable { @Nullable private IntSet mPromiseIconIds; - public InstallSessionHelper(@NonNull final Context context) { + @Inject + public InstallSessionHelper(@NonNull @ApplicationContext final Context context, + DaggerSingletonTracker tracker) { mInstaller = context.getPackageManager().getPackageInstaller(); mAppContext = context.getApplicationContext(); mLauncherApps = context.getSystemService(LauncherApps.class); + ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this)); } @Override diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt index ca2ef42124..a991981362 100644 --- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt +++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt @@ -28,6 +28,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.PROMISE_ICON_IDS +import com.android.launcher3.util.DaggerSingletonTracker import com.android.launcher3.util.Executors.MODEL_EXECUTOR import com.android.launcher3.util.IntArray import com.android.launcher3.util.LauncherModelHelper @@ -53,6 +54,7 @@ class InstallSessionHelperTest { private val expectedAppPackage = "expectedAppPackage" private val expectedInstallerPackage = "expectedInstallerPackage" private val mockPackageInstaller: PackageInstaller = mock() + private val mTracker: DaggerSingletonTracker = mock() private lateinit var installSessionHelper: InstallSessionHelper private lateinit var launcherApps: LauncherApps @@ -62,7 +64,7 @@ class InstallSessionHelperTest { whenever(packageManager.packageInstaller).thenReturn(mockPackageInstaller) whenever(sandboxContext.packageName).thenReturn(expectedInstallerPackage) launcherApps = sandboxContext.spyService(LauncherApps::class.java) - installSessionHelper = InstallSessionHelper(sandboxContext) + installSessionHelper = InstallSessionHelper(sandboxContext, mTracker) } @Test diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 68004bb058..cee88ac047 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -26,6 +26,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -103,6 +104,8 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT; private static final String TAG = "AbstractLauncherUiTest"; + private static final long BYTES_PER_MEGABYTE = 1 << 20; + private static boolean sDumpWasGenerated = false; private static boolean sActivityLeakReported = false; private static boolean sSeenKeyguard = false; @@ -124,6 +127,10 @@ public abstract class AbstractLauncherUiTest { protected String mTargetPackage; private int mLauncherPid; + private final ActivityManager.MemoryInfo mMemoryInfo = new ActivityManager.MemoryInfo(); + private final ActivityManager mActivityManager; + private long mMemoryBefore; + /** Detects activity leaks and throws an exception if a leak is found. */ public static void checkDetectedLeaks(LauncherInstrumentation launcher) { checkDetectedLeaks(launcher, false); @@ -192,6 +199,8 @@ public abstract class AbstractLauncherUiTest { } protected AbstractLauncherUiTest() { + mActivityManager = InstrumentationRegistry.getContext() + .getSystemService(ActivityManager.class); mLauncher.enableCheckEventsForSuccessfulGestures(); mLauncher.setAnomalyChecker(AbstractLauncherUiTest::verifyKeyguardInvisible); try { @@ -311,6 +320,26 @@ public abstract class AbstractLauncherUiTest { initialize(this); } + private long getAvailableMemory() { + mActivityManager.getMemoryInfo(mMemoryInfo); + + return Math.divideExact(mMemoryInfo.availMem, BYTES_PER_MEGABYTE); + } + + @Before + public void saveMemoryBefore() { + mMemoryBefore = getAvailableMemory(); + } + + @After + public void logMemoryAfter() { + long memoryAfter = getAvailableMemory(); + + Log.d(TAG, "Available memory: before=" + mMemoryBefore + + "MB, after=" + memoryAfter + + "MB, delta=" + (memoryAfter - mMemoryBefore) + "MB"); + } + /** Method that should be called when a test starts. */ public static void onTestStart() { waitForSetupWizardDismissal();