From 9420ba75157bfd659571ee2ee1ae42ce9c0bbe12 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 9 Sep 2024 15:20:14 +0100 Subject: [PATCH] Separate desktop and fullscreen carousel - setRunningTaskHidden now changes attachAlpha only, stableAlpha is untouched as part of the attach animation, so both alpha can be set independently without interferring with each other - During swipe up, hide task of differnt type (fullscreen/desktop) from the carousel, apply unhide when the gesture settles - Update the min/max alpha calculation when a type of task is hidden from the carousel Fix: 353950224 Test: Quick switch, swipe up, and scroll in Overview with and without Desktop tile, on phone and tablet Test: presubmit test on quickswitch and swipe up Flag: com.android.launcher3.enable_large_desktop_windowing_tile Change-Id: I0a63da3ee3e55f4d1edbc53b2a4c5fccda2af387 --- .../quickstep/util/RecentsViewUtils.kt | 81 +++++++++++++--- .../android/quickstep/views/RecentsView.java | 93 ++++++++++++------- .../com/android/quickstep/views/TaskView.kt | 76 +++++++-------- 3 files changed, 161 insertions(+), 89 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt index 595aa0077b..be1af64f02 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt @@ -28,9 +28,17 @@ import com.android.systemui.shared.recents.model.ThumbnailData * and extracted functions from RecentsView to facilitate the implementation of unit tests. */ class RecentsViewUtils { + /** Takes a screenshot of all [taskView] and return map of taskId to the screenshot */ + fun screenshotTasks( + taskView: TaskView, + recentsAnimationController: RecentsAnimationController, + ): Map = + taskView.taskContainers.associate { + it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id) + } /** - * Sort task groups to move desktop tasks to the end of the list. + * Sorts task groups to move desktop tasks to the end of the list. * * @param tasks List of group tasks to be sorted. * @return Sorted list of GroupTasks to be used in the RecentsView. @@ -40,6 +48,7 @@ class RecentsViewUtils { return otherTasks + desktopTasks } + /** Returns the expected index of the focus task. */ fun getFocusedTaskIndex(taskGroups: List): Int { // The focused task index is placed after the desktop tasks views. return if (enableLargeDesktopWindowingTile()) { @@ -49,12 +58,8 @@ class RecentsViewUtils { } } - /** - * Counts [TaskView]s that are [DesktopTaskView] instances. - * - * @param taskViews List of [TaskView]s - */ - fun getDesktopTaskViewCount(taskViews: List): Int = + /** Counts [TaskView]s that are [DesktopTaskView] instances. */ + fun getDesktopTaskViewCount(taskViews: Iterable): Int = taskViews.count { it is DesktopTaskView } /** Returns a list of all large TaskView Ids from [TaskView]s */ @@ -66,18 +71,64 @@ class RecentsViewUtils { * * @param taskViews List of [TaskView]s */ - fun getFirstLargeTaskView(taskViews: List): TaskView? = + fun getFirstLargeTaskView(taskViews: Iterable): TaskView? = taskViews.firstOrNull { it.isLargeTile } - fun screenshotTasks( - taskView: TaskView, - recentsAnimationController: RecentsAnimationController, - ): Map = - taskView.taskContainers.associate { - it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id) + /** Returns the last TaskView that should be displayed as a large tile. */ + fun getLastLargeTaskView(taskViews: Iterable): TaskView? = + taskViews.lastOrNull { it.isLargeTile } + + /** Returns the first [TaskView], with some tasks possibly hidden in the carousel. */ + fun getFirstTaskViewInCarousel( + nonRunningTaskCategoryHidden: Boolean, + taskViews: Iterable, + runningTaskView: TaskView?, + ): TaskView? = + taskViews.firstOrNull { + it.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) + } + + /** Returns the last [TaskView], with some tasks possibly hidden in the carousel. */ + fun getLastTaskViewInCarousel( + nonRunningTaskCategoryHidden: Boolean, + taskViews: Iterable, + runningTaskView: TaskView?, + ): TaskView? = + taskViews.lastOrNull { + it.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) } /** Returns the current list of [TaskView] children. */ - fun getTaskViews(taskViewCount: Int, requireTaskViewAt: (Int) -> TaskView): List = + fun getTaskViews(taskViewCount: Int, requireTaskViewAt: (Int) -> TaskView): Iterable = (0 until taskViewCount).map(requireTaskViewAt) + + /** Apply attachAlpha to all [TaskView] accordingly to different conditions. */ + fun applyAttachAlpha( + taskViews: Iterable, + runningTaskView: TaskView?, + runningTaskTileHidden: Boolean, + nonRunningTaskCategoryHidden: Boolean, + ) { + taskViews.forEach { taskView -> + val isVisible = + if (taskView == runningTaskView) !runningTaskTileHidden + else taskView.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) + taskView.attachAlpha = if (isVisible) 1f else 0f + } + } + + private fun TaskView.isVisibleInCarousel( + runningTaskView: TaskView?, + nonRunningTaskCategoryHidden: Boolean, + ): Boolean = + if (!nonRunningTaskCategoryHidden) true + else if (runningTaskView == null) true else getCategory() == runningTaskView.getCategory() + + private fun TaskView.getCategory(): TaskViewCategory = + if (this is DesktopTaskView) TaskViewCategory.DESKTOP else TaskViewCategory.FULL_SCREEN + + private enum class TaskViewCategory { + FULL_SCREEN, + DESKTOP, + } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index e37e036857..bb46a2c3ed 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -681,6 +681,7 @@ public abstract class RecentsView< protected int mRunningTaskViewId = -1; private int mTaskViewIdCount; protected boolean mRunningTaskTileHidden; + private boolean mNonRunningTaskCategoryHidden; @Nullable private Task[] mTmpRunningTasks; protected int mFocusedTaskViewId = INVALID_TASK_ID; @@ -2094,14 +2095,10 @@ public abstract class RecentsView< simulator.fullScreenProgress.value = 0; simulator.recentsViewScale.value = 1; }); - // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is - // null. - if (!mRunningTaskShowScreenshot) { - setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot); - } - if (mRunningTaskTileHidden) { - setRunningTaskHidden(mRunningTaskTileHidden); - } + // Reapply runningTask related attributes as they might have been reset by + // resetViewTransforms(). + setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot); + applyAttachAlpha(); updateCurveProperties(); // Update the set of visible task's data @@ -2650,10 +2647,6 @@ public abstract class RecentsView< return getTaskViewFromTaskViewId(mFocusedTaskViewId); } - private @Nullable TaskView getFirstLargeTaskView() { - return mUtils.getFirstLargeTaskView(getTaskViews()); - } - @Nullable private TaskView getTaskViewFromTaskViewId(int taskViewId) { if (taskViewId == -1) { @@ -2749,7 +2742,10 @@ public abstract class RecentsView< showCurrentTask(mActiveGestureRunningTasks); setEnableFreeScroll(false); setEnableDrawingLiveTile(false); - setRunningTaskHidden(!shouldUpdateRunningTaskAlpha()); + setRunningTaskHidden(true); + if (enableLargeDesktopWindowingTile()) { + setNonRunningTaskCategoryHidden(true); + } setTaskIconScaledDown(true); } @@ -2888,6 +2884,9 @@ public abstract class RecentsView< setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS); Log.d(TAG, "onGestureAnimationEnd - mEnableDrawingLiveTile: " + mEnableDrawingLiveTile); setRunningTaskHidden(false); + if (enableLargeDesktopWindowingTile()) { + setNonRunningTaskCategoryHidden(false); + } animateUpTaskIconScale(); animateActionsViewIn(); @@ -3043,13 +3042,27 @@ public abstract class RecentsView< if (runningTask == null) { return; } - runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha); + applyAttachAlpha(); if (!isHidden) { AccessibilityManagerCompat.sendCustomAccessibilityEvent( runningTask, AccessibilityEvent.TYPE_VIEW_FOCUSED, null); } } + /** + * Hides the tasks that has a different category (Fullscreen/Desktop) from the running task. + */ + public void setNonRunningTaskCategoryHidden(boolean isHidden) { + mNonRunningTaskCategoryHidden = isHidden; + updateMinAndMaxScrollX(); + applyAttachAlpha(); + } + + private void applyAttachAlpha() { + mUtils.applyAttachAlpha(getTaskViews(), getRunningTaskView(), mRunningTaskTileHidden, + mNonRunningTaskCategoryHidden); + } + private void setRunningTaskViewShowScreenshot(boolean showScreenshot) { setRunningTaskViewShowScreenshot(showScreenshot, /*updatedThumbnails=*/null); } @@ -4447,11 +4460,7 @@ public abstract class RecentsView< alpha = Utilities.boundToRange(alpha, 0, 1); mContentAlpha = alpha; - TaskView runningTaskView = getRunningTaskView(); for (TaskView taskView : getTaskViews()) { - if (runningTaskView != null && mRunningTaskTileHidden && taskView == runningTaskView) { - continue; - } taskView.setStableAlpha(alpha); } mClearAllButton.setContentAlpha(mContentAlpha); @@ -4564,7 +4573,7 @@ public abstract class RecentsView< /** * Returns the current list of [TaskView] children. */ - private List getTaskViews() { + private Iterable getTaskViews() { return mUtils.getTaskViews(getTaskViewCount(), this::requireTaskViewAt); } @@ -5794,26 +5803,42 @@ public abstract class RecentsView< } private int getFirstViewIndex() { - TaskView firstTaskView = mShowAsGridLastOnLayout ? getFirstLargeTaskView() : null; - return firstTaskView != null ? indexOfChild(firstTaskView) : 0; + final TaskView firstView; + if (mShowAsGridLastOnLayout) { + // For grid Overivew, it always start if a large tile (focused task or desktop task) if + // they exist, otherwise it start with the first task. + TaskView firstLargeTaskView = mUtils.getFirstLargeTaskView(getTaskViews()); + if (firstLargeTaskView != null) { + firstView = firstLargeTaskView; + } else { + firstView = getTaskViewAt(0); + } + } else { + firstView = mUtils.getFirstTaskViewInCarousel(mNonRunningTaskCategoryHidden, + getTaskViews(), getRunningTaskView()); + } + return indexOfChild(firstView); } private int getLastViewIndex() { + final View lastView; if (!mDisallowScrollToClearAll) { - return indexOfChild(mClearAllButton); + // When ClearAllButton is present, it always end with ClearAllButton. + lastView = mClearAllButton; + } else if (mShowAsGridLastOnLayout) { + // When ClearAllButton is absent, for the grid Overview, it always end with a grid task + // if they exist, otherwise it ends with a large tile (focused task or desktop task). + TaskView lastGridTaskView = getLastGridTaskView(); + if (lastGridTaskView != null) { + lastView = lastGridTaskView; + } else { + lastView = mUtils.getLastLargeTaskView(getTaskViews()); + } + } else { + lastView = mUtils.getLastTaskViewInCarousel(mNonRunningTaskCategoryHidden, + getTaskViews(), getRunningTaskView()); } - - if (!mShowAsGridLastOnLayout) { - return getTaskViewCount() - 1; - } - - TaskView lastGridTaskView = getLastGridTaskView(); - if (lastGridTaskView != null) { - return indexOfChild(lastGridTaskView); - } - - // Returns focus task if there are no grid tasks. - return indexOfChild(getFirstLargeTaskView()); + return indexOfChild(lastView); } /** diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index 601dae8b27..4dcf2e7cc5 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -84,7 +84,6 @@ import com.android.quickstep.TaskViewUtils import com.android.quickstep.orientation.RecentsPagedOrientationHandler import com.android.quickstep.recents.di.RecentsDependencies import com.android.quickstep.recents.di.get -import com.android.quickstep.task.thumbnail.TaskThumbnailView import com.android.quickstep.task.viewmodel.TaskViewModel import com.android.quickstep.util.ActiveGestureErrorDetector import com.android.quickstep.util.ActiveGestureLog @@ -201,14 +200,14 @@ constructor( get() = pagedOrientationHandler.getPrimaryValue( SPLIT_SELECT_TRANSLATION_X, - SPLIT_SELECT_TRANSLATION_Y + SPLIT_SELECT_TRANSLATION_Y, ) protected val secondarySplitTranslationProperty: FloatProperty get() = pagedOrientationHandler.getSecondaryValue( SPLIT_SELECT_TRANSLATION_X, - SPLIT_SELECT_TRANSLATION_Y + SPLIT_SELECT_TRANSLATION_Y, ) protected val primaryDismissTranslationProperty: FloatProperty @@ -223,21 +222,21 @@ constructor( get() = pagedOrientationHandler.getPrimaryValue( TASK_OFFSET_TRANSLATION_X, - TASK_OFFSET_TRANSLATION_Y + TASK_OFFSET_TRANSLATION_Y, ) protected val secondaryTaskOffsetTranslationProperty: FloatProperty get() = pagedOrientationHandler.getSecondaryValue( TASK_OFFSET_TRANSLATION_X, - TASK_OFFSET_TRANSLATION_Y + TASK_OFFSET_TRANSLATION_Y, ) protected val taskResistanceTranslationProperty: FloatProperty get() = pagedOrientationHandler.getSecondaryValue( TASK_RESISTANCE_TRANSLATION_X, - TASK_RESISTANCE_TRANSLATION_Y + TASK_RESISTANCE_TRANSLATION_Y, ) private val tempCoordinates = FloatArray(2) @@ -399,7 +398,7 @@ constructor( } get() = taskViewAlpha.get(ALPHA_INDEX_STABLE).value - protected var attachAlpha + var attachAlpha set(value) { taskViewAlpha.get(ALPHA_INDEX_ATTACH).value = value } @@ -435,7 +434,7 @@ constructor( field = value Log.d( TAG, - "${taskIds.contentToString()} - setting border animator visibility to: $field" + "${taskIds.contentToString()} - setting border animator visibility to: $field", ) hoverBorderAnimator?.setBorderVisibility(visible = field, animated = true) } @@ -455,7 +454,7 @@ constructor( FOCUS_TRANSITION, FOCUS_TRANSITION_INDEX_COUNT, { x: Float, y: Float -> x * y }, - 1f + 1f, ) private val focusTransitionFullscreen = focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_FULLSCREEN) @@ -503,8 +502,8 @@ constructor( this, it.getColor( R.styleable.TaskView_focusBorderColor, - BorderAnimator.DEFAULT_BORDER_COLOR - ) + BorderAnimator.DEFAULT_BORDER_COLOR, + ), ) else null this.hoverBorderAnimator = @@ -519,8 +518,8 @@ constructor( this, it.getColor( R.styleable.TaskView_hoverBorderColor, - BorderAnimator.DEFAULT_BORDER_COLOR - ) + BorderAnimator.DEFAULT_BORDER_COLOR, + ), ) else null } @@ -634,7 +633,7 @@ constructor( addAction( AccessibilityAction( R.id.action_close, - context.getText(R.string.accessibility_close) + context.getText(R.string.accessibility_close), ) ) @@ -658,7 +657,7 @@ constructor( 1, it.taskViewCount - it.indexOfChild(this@TaskView) - 1, 1, - false + false, ) } } @@ -704,7 +703,7 @@ constructor( R.id.show_windows, R.id.digital_wellbeing_toast, STAGE_POSITION_UNDEFINED, - taskOverlayFactory + taskOverlayFactory, ) ) taskContainers.forEach { it.bind() } @@ -742,7 +741,7 @@ constructor( stagePosition, digitalWellBeingToast, findViewById(showWindowViewId)!!, - taskOverlayFactory + taskOverlayFactory, ) } @@ -860,7 +859,7 @@ constructor( if (relativeToDragLayer) { container.dragLayer.getDescendantRectRelativeToSelf( it.snapshotView, - thumbnailBounds + thumbnailBounds, ) } else { thumbnailBounds.set(it.snapshotView) @@ -979,7 +978,7 @@ constructor( @JvmOverloads open fun setShouldShowScreenshot( shouldShowScreenshot: Boolean, - thumbnailDatas: Map? = null + thumbnailDatas: Map? = null, ) { if (this.shouldShowScreenshot == shouldShowScreenshot) return this.shouldShowScreenshot = shouldShowScreenshot @@ -1030,7 +1029,7 @@ constructor( if (!isClickableAsLiveTile) { Log.e( TAG, - "launchAsLiveTile - TaskView is not clickable as a live tile; returning to home: ${taskIds.contentToString()}" + "launchAsLiveTile - TaskView is not clickable as a live tile; returning to home: ${taskIds.contentToString()}", ) return null } @@ -1049,7 +1048,7 @@ constructor( apps.toTypedArray(), wallpapers.toTypedArray(), remoteTargetHandles[0].transformParams.targetSet.nonApps, - remoteTargetHandles[0].transformParams.targetSet.targetMode + remoteTargetHandles[0].transformParams.targetSet.targetMode, ) } if (targets == null) { @@ -1059,7 +1058,7 @@ constructor( if (runnableList == null) { Log.e( TAG, - "launchAsLiveTile - Recents animation cancelled and cannot launch task as non-live tile; returning to home: ${taskIds.contentToString()}" + "launchAsLiveTile - Recents animation cancelled and cannot launch task as non-live tile; returning to home: ${taskIds.contentToString()}", ) } isClickableAsLiveTile = true @@ -1068,7 +1067,7 @@ constructor( TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "composeRecentsLaunchAnimator", - taskIds.contentToString() + taskIds.contentToString(), ) val runnableList = RunnableList() with(AnimatorSet()) { @@ -1081,7 +1080,7 @@ constructor( true /* launcherClosing */, recentsView.stateManager, recentsView, - recentsView.depthController + recentsView.depthController, ) addListener( object : AnimatorListenerAdapter() { @@ -1118,7 +1117,7 @@ constructor( TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", - taskIds.contentToString() + taskIds.contentToString(), ) val opts = container.getActivityLaunchOptions(this, null).apply { @@ -1130,7 +1129,7 @@ constructor( ) { Log.d( TAG, - "launchAsStaticTile - startActivityFromRecents: ${taskIds.contentToString()}" + "launchAsStaticTile - startActivityFromRecents: ${taskIds.contentToString()}", ) ActiveGestureLog.INSTANCE.trackEvent( ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED @@ -1163,12 +1162,12 @@ constructor( @JvmOverloads open fun launchWithoutAnimation( isQuickSwitch: Boolean = false, - callback: (launched: Boolean) -> Unit + callback: (launched: Boolean) -> Unit, ) { TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", - taskIds.contentToString() + taskIds.contentToString(), ) val firstContainer = taskContainers[0] val failureListener = TaskRemovedDuringLaunchListener(context.applicationContext) @@ -1199,7 +1198,7 @@ constructor( 0, 0, Executors.MAIN_EXECUTOR.handler, - { callback(true) } + { callback(true) }, ) { failureListener.onTransitionFinished() } @@ -1227,7 +1226,7 @@ constructor( } Log.d( TAG, - "launchWithoutAnimation - startActivityFromRecents: ${taskIds.contentToString()}" + "launchWithoutAnimation - startActivityFromRecents: ${taskIds.contentToString()}", ) } } @@ -1246,7 +1245,7 @@ constructor( recentsView?.initiateSplitSelect( this, splitPositionOption.stagePosition, - SplitConfigurationOptions.getLogEventForPosition(splitPositionOption.stagePosition) + SplitConfigurationOptions.getLogEventForPosition(splitPositionOption.stagePosition), ) } @@ -1269,7 +1268,7 @@ constructor( container.splitAnimationThumbnail, /* intent */ null, /* user */ null, - container.itemInfo + container.itemInfo, ) } @@ -1374,13 +1373,13 @@ constructor( this[0] = viewHalfWidth this[1] = viewHalfHeight }, - false + false, ) transformingTouchDelegate.setBounds( (tempCenterCoordinates[0] - viewHalfWidth).toInt(), (tempCenterCoordinates[1] - viewHalfHeight).toInt(), (tempCenterCoordinates[0] + viewHalfWidth).toInt(), - (tempCenterCoordinates[1] + viewHalfHeight).toInt() + (tempCenterCoordinates[1] + viewHalfHeight).toInt(), ) } @@ -1390,7 +1389,7 @@ constructor( it.showWindowsView?.let { showWindowsView -> updateFilterCallback( showWindowsView, - getFilterUpdateCallback(it.task.key.packageName) + getFilterUpdateCallback(it.task.key.packageName), ) } } @@ -1593,10 +1592,7 @@ constructor( resetViewTransforms() } - fun getTaskContainerForTaskThumbnailView(taskThumbnailView: TaskThumbnailView): TaskContainer? = - taskContainers.firstOrNull { it.thumbnailView == taskThumbnailView } - - open fun resetViewTransforms() { + fun resetViewTransforms() { // fullscreenTranslation and accumulatedTranslation should not be reset, as // resetViewTransforms is called during QuickSwitch scrolling. dismissTranslationX = 0f @@ -1710,7 +1706,7 @@ constructor( Interpolators.clampToProgress( Interpolators.FAST_OUT_SLOW_IN, 1f - FOCUS_TRANSITION_THRESHOLD, - 1f + 1f, )!! private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())