1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.quickstep.util 18 19 import com.android.launcher3.Flags.enableLargeDesktopWindowingTile 20 import com.android.quickstep.RecentsAnimationController 21 import com.android.quickstep.views.DesktopTaskView 22 import com.android.quickstep.views.TaskView 23 import com.android.quickstep.views.TaskViewType 24 import com.android.systemui.shared.recents.model.ThumbnailData 25 26 /** 27 * Helper class for [com.android.quickstep.views.RecentsView]. This util class contains refactored 28 * and extracted functions from RecentsView to facilitate the implementation of unit tests. 29 */ 30 class RecentsViewUtils { 31 /** Takes a screenshot of all [taskView] and return map of taskId to the screenshot */ 32 fun screenshotTasks( 33 taskView: TaskView, 34 recentsAnimationController: RecentsAnimationController, 35 ): Map<Int, ThumbnailData> = 36 taskView.taskContainers.associate { 37 it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id) 38 } 39 40 /** 41 * Sorts task groups to move desktop tasks to the end of the list. 42 * 43 * @param tasks List of group tasks to be sorted. 44 * @return Sorted list of GroupTasks to be used in the RecentsView. 45 */ 46 fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> { 47 val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP } 48 return otherTasks + desktopTasks 49 } 50 51 /** Counts [TaskView]s that are [DesktopTaskView] instances. */ 52 fun getDesktopTaskViewCount(taskViews: Iterable<TaskView>): Int = 53 taskViews.count { it is DesktopTaskView } 54 55 /** Returns a list of all large TaskView Ids from [TaskView]s */ 56 fun getLargeTaskViewIds(taskViews: Iterable<TaskView>): List<Int> = 57 taskViews.filter { it.isLargeTile }.map { it.taskViewId } 58 59 /** Counts [TaskView]s that are large tiles. */ 60 fun getLargeTileCount(taskViews: Iterable<TaskView>): Int = taskViews.count { it.isLargeTile } 61 62 /** 63 * Returns the first TaskView that should be displayed as a large tile. 64 * 65 * @param taskViews List of [TaskView]s 66 * @param splitSelectActive current split state 67 */ 68 fun getFirstLargeTaskView( 69 taskViews: MutableIterable<TaskView>, 70 splitSelectActive: Boolean, 71 ): TaskView? = 72 taskViews.firstOrNull { it.isLargeTile && !(splitSelectActive && it is DesktopTaskView) } 73 74 /** Returns the expected focus task. */ 75 fun getExpectedFocusedTask(taskViews: Iterable<TaskView>): TaskView? = 76 if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView } 77 else taskViews.firstOrNull() 78 79 /** 80 * Returns the [TaskView] that should be the current page during task binding, in the following 81 * priorities: 82 * 1. Running task 83 * 2. Focused task 84 * 3. First non-desktop task 85 * 4. Last desktop task 86 * 5. null otherwise 87 */ 88 fun getExpectedCurrentTask( 89 runningTaskView: TaskView?, 90 focusedTaskView: TaskView?, 91 taskViews: Iterable<TaskView>, 92 ): TaskView? = 93 runningTaskView 94 ?: focusedTaskView 95 ?: taskViews.firstOrNull { it !is DesktopTaskView } 96 ?: taskViews.lastOrNull() 97 98 /** 99 * Returns the first TaskView that is not large 100 * 101 * @param taskViews List of [TaskView]s 102 */ 103 fun getFirstSmallTaskView(taskViews: MutableIterable<TaskView>): TaskView? = 104 taskViews.firstOrNull { !it.isLargeTile } 105 106 /** Returns the last TaskView that should be displayed as a large tile. */ 107 fun getLastLargeTaskView(taskViews: Iterable<TaskView>): TaskView? = 108 taskViews.lastOrNull { it.isLargeTile } 109 110 /** Returns the first [TaskView], with some tasks possibly hidden in the carousel. */ 111 fun getFirstTaskViewInCarousel( 112 nonRunningTaskCarouselHidden: Boolean, 113 taskViews: Iterable<TaskView>, 114 runningTaskView: TaskView?, 115 ): TaskView? = 116 taskViews.firstOrNull { 117 it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden) 118 } 119 120 /** Returns the last [TaskView], with some tasks possibly hidden in the carousel. */ 121 fun getLastTaskViewInCarousel( 122 nonRunningTaskCarouselHidden: Boolean, 123 taskViews: Iterable<TaskView>, 124 runningTaskView: TaskView?, 125 ): TaskView? = 126 taskViews.lastOrNull { 127 it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden) 128 } 129 130 /** Returns if any small tasks are fully visible */ 131 fun isAnySmallTaskFullyVisible( 132 taskViews: Iterable<TaskView>, 133 isTaskViewFullyVisible: (TaskView) -> Boolean, 134 ): Boolean = taskViews.any { !it.isLargeTile && isTaskViewFullyVisible(it) } 135 136 /** Returns the current list of [TaskView] children. */ 137 fun getTaskViews(taskViewCount: Int, requireTaskViewAt: (Int) -> TaskView): Iterable<TaskView> = 138 (0 until taskViewCount).map(requireTaskViewAt) 139 140 /** Apply attachAlpha to all [TaskView] accordingly to different conditions. */ 141 fun applyAttachAlpha( 142 taskViews: Iterable<TaskView>, 143 runningTaskView: TaskView?, 144 runningTaskAttachAlpha: Float, 145 nonRunningTaskCarouselHidden: Boolean, 146 ) { 147 taskViews.forEach { taskView -> 148 taskView.attachAlpha = 149 if (taskView == runningTaskView) { 150 runningTaskAttachAlpha 151 } else { 152 if (taskView.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden)) 153 1f 154 else 0f 155 } 156 } 157 } 158 159 fun TaskView.isVisibleInCarousel( 160 runningTaskView: TaskView?, 161 nonRunningTaskCarouselHidden: Boolean, 162 ): Boolean = 163 if (!nonRunningTaskCarouselHidden) true 164 else getCarouselType() == runningTaskView.getCarouselType() 165 166 /** Returns the carousel type of the TaskView, and default to fullscreen if it's null. */ 167 private fun TaskView?.getCarouselType(): TaskViewCarousel = 168 if (this is DesktopTaskView) TaskViewCarousel.DESKTOP else TaskViewCarousel.FULL_SCREEN 169 170 private enum class TaskViewCarousel { 171 FULL_SCREEN, 172 DESKTOP, 173 } 174 } 175