1 /* 2 * 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.views 18 19 import android.graphics.Bitmap 20 import android.graphics.Matrix 21 import android.util.Log 22 import android.view.View 23 import android.view.View.OnClickListener 24 import com.android.app.tracing.traceSection 25 import com.android.launcher3.Flags.enableOverviewIconMenu 26 import com.android.launcher3.Flags.enableRefactorTaskThumbnail 27 import com.android.launcher3.model.data.TaskViewItemInfo 28 import com.android.launcher3.util.SplitConfigurationOptions 29 import com.android.launcher3.util.TransformingTouchDelegate 30 import com.android.quickstep.TaskOverlayFactory 31 import com.android.quickstep.ViewUtils.addAccessibleChildToList 32 import com.android.quickstep.recents.domain.usecase.ThumbnailPosition 33 import com.android.quickstep.recents.ui.mapper.TaskUiStateMapper 34 import com.android.quickstep.recents.ui.viewmodel.TaskData 35 import com.android.quickstep.task.thumbnail.TaskThumbnailView 36 import com.android.systemui.shared.recents.model.Task 37 import com.android.systemui.shared.recents.model.ThumbnailData 38 39 /** Holder for all Task dependent information. */ 40 class TaskContainer( 41 val taskView: TaskView, 42 val task: Task, 43 val snapshotView: View, 44 val iconView: TaskViewIcon, 45 /** 46 * This technically can be a vanilla [android.view.TouchDelegate] class, however that class 47 * requires setting the touch bounds at construction, so we'd repeatedly be created many 48 * instances unnecessarily as scrolling occurs, whereas [TransformingTouchDelegate] allows touch 49 * delegated bounds only to be updated. 50 */ 51 val iconTouchDelegate: TransformingTouchDelegate, 52 /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */ 53 @SplitConfigurationOptions.StagePosition val stagePosition: Int, 54 val digitalWellBeingToast: DigitalWellBeingToast?, 55 val showWindowsView: View?, 56 taskOverlayFactory: TaskOverlayFactory, 57 ) { 58 val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this) 59 var thumbnailPosition: ThumbnailPosition? = null 60 private var overlayEnabledStatus = false 61 62 init { 63 if (enableRefactorTaskThumbnail()) { 64 require(snapshotView is TaskThumbnailView) 65 } else { 66 require(snapshotView is TaskThumbnailViewDeprecated) 67 } 68 } 69 70 internal var thumbnailData: ThumbnailData? = null 71 private set 72 73 val thumbnail: Bitmap? 74 /** If possible don't use this. It should be replaced as part of b/331753115. */ 75 get() = 76 if (enableRefactorTaskThumbnail()) thumbnailData?.thumbnail 77 else thumbnailViewDeprecated.thumbnail 78 79 val thumbnailView: TaskThumbnailView 80 get() { 81 require(enableRefactorTaskThumbnail()) 82 return snapshotView as TaskThumbnailView 83 } 84 85 val thumbnailViewDeprecated: TaskThumbnailViewDeprecated 86 get() { 87 require(!enableRefactorTaskThumbnail()) 88 return snapshotView as TaskThumbnailViewDeprecated 89 } 90 91 var isThumbnailValid: Boolean = false 92 internal set 93 94 val shouldShowSplashView: Boolean 95 get() = 96 if (enableRefactorTaskThumbnail()) taskView.shouldShowSplash() 97 else thumbnailViewDeprecated.shouldShowSplashView() 98 99 /** Builds proto for logging */ 100 val itemInfo: TaskViewItemInfo 101 get() = TaskViewItemInfo(taskView, this) 102 bindnull103 fun bind() = 104 traceSection("TaskContainer.bind") { 105 digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition) 106 if (!enableRefactorTaskThumbnail()) { 107 thumbnailViewDeprecated.bind(task, overlay, taskView) 108 } 109 } 110 destroynull111 fun destroy() = 112 traceSection("TaskContainer.destroy") { 113 digitalWellBeingToast?.destroy() 114 snapshotView.scaleX = 1f 115 snapshotView.scaleY = 1f 116 overlay.reset() 117 if (enableRefactorTaskThumbnail()) { 118 isThumbnailValid = false 119 thumbnailData = null 120 thumbnailView.onRecycle() 121 } else { 122 thumbnailViewDeprecated.setShowSplashForSplitSelection(false) 123 } 124 125 if (enableOverviewIconMenu()) { 126 (iconView as IconAppChipView).reset() 127 } 128 } 129 setOverlayEnablednull130 fun setOverlayEnabled(enabled: Boolean) { 131 if (!enableRefactorTaskThumbnail()) { 132 thumbnailViewDeprecated.setOverlayEnabled(enabled) 133 } 134 } 135 setOverlayEnablednull136 fun setOverlayEnabled(enabled: Boolean, thumbnailPosition: ThumbnailPosition?) { 137 if (enableRefactorTaskThumbnail()) { 138 if (overlayEnabledStatus != enabled || this.thumbnailPosition != thumbnailPosition) { 139 overlayEnabledStatus = enabled 140 141 refreshOverlay(thumbnailPosition) 142 } 143 } 144 } 145 refreshOverlaynull146 fun refreshOverlay(thumbnailPosition: ThumbnailPosition?) = 147 traceSection("TaskContainer.refreshOverlay") { 148 this.thumbnailPosition = thumbnailPosition 149 when { 150 !overlayEnabledStatus -> overlay.reset() 151 thumbnailPosition == null -> { 152 Log.e(TAG, "Thumbnail position was null during overlay refresh", Exception()) 153 overlay.reset() 154 } 155 else -> 156 overlay.initOverlay( 157 task, 158 thumbnailData?.thumbnail, 159 thumbnailPosition.matrix, 160 thumbnailPosition.isRotated, 161 ) 162 } 163 } 164 addChildForAccessibilitynull165 fun addChildForAccessibility(outChildren: ArrayList<View>) { 166 addAccessibleChildToList(iconView.asView(), outChildren) 167 addAccessibleChildToList(snapshotView, outChildren) 168 showWindowsView?.let { addAccessibleChildToList(it, outChildren) } 169 digitalWellBeingToast?.let { addAccessibleChildToList(it, outChildren) } 170 overlay.addChildForAccessibility(outChildren) 171 } 172 setStatenull173 fun setState( 174 state: TaskData?, 175 liveTile: Boolean, 176 hasHeader: Boolean, 177 clickCloseListener: OnClickListener?, 178 ) = 179 traceSection("TaskContainer.setState") { 180 thumbnailView.setState( 181 TaskUiStateMapper.toTaskThumbnailUiState( 182 state, 183 liveTile, 184 hasHeader, 185 clickCloseListener, 186 ), 187 state?.taskId, 188 ) 189 thumbnailData = if (state is TaskData.Data) state.thumbnailData else null 190 overlay.setThumbnailState(thumbnailData) 191 } 192 updateTintAmountnull193 fun updateTintAmount(tintAmount: Float) { 194 thumbnailView.updateTintAmount(tintAmount) 195 } 196 197 /** 198 * Updates the progress of the menu opening animation. 199 * 200 * This function propagates the given `progress` value to the `thumbnailView` allowing the 201 * thumbnail view to animate its visual state in sync with the menu's opening/closing 202 * transition. 203 * 204 * @param progress The progress of the menu opening animation (from closed=0 to fully open=1) 205 */ updateMenuOpenProgressnull206 fun updateMenuOpenProgress(progress: Float) { 207 thumbnailView.updateMenuOpenProgress(progress) 208 } 209 210 /** 211 * Updates the thumbnail splash progress for a given task. 212 * 213 * This function manages the visual feedback of a "splash" effect that can be displayed over a 214 * thumbnail image, typically during loading or updating. It calculates the alpha (transparency) 215 * of the splash based on the provided progress and then applies this alpha to the thumbnail 216 * view if it should be displayed. 217 * 218 * @param progress The progress of the operation, ranging from 0.0 to 1.0 219 */ updateThumbnailSplashProgressnull220 fun updateThumbnailSplashProgress(progress: Float) { 221 if (enableRefactorTaskThumbnail()) { 222 thumbnailView.updateSplashAlpha(progress) 223 } else { 224 thumbnailViewDeprecated.setSplashAlpha(progress) 225 } 226 } 227 updateThumbnailMatrixnull228 fun updateThumbnailMatrix(matrix: Matrix) { 229 thumbnailView.setImageMatrix(matrix) 230 } 231 232 companion object { 233 const val TAG = "TaskContainer" 234 } 235 } 236