• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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