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.view.View
21 import com.android.launcher3.Flags.enableRefactorTaskThumbnail
22 import com.android.launcher3.model.data.TaskViewItemInfo
23 import com.android.launcher3.util.SplitConfigurationOptions
24 import com.android.launcher3.util.TransformingTouchDelegate
25 import com.android.quickstep.TaskOverlayFactory
26 import com.android.quickstep.ViewUtils.addAccessibleChildToList
27 import com.android.quickstep.recents.di.RecentsDependencies
28 import com.android.quickstep.recents.di.get
29 import com.android.quickstep.recents.di.getScope
30 import com.android.quickstep.recents.di.inject
31 import com.android.quickstep.recents.viewmodel.TaskContainerViewModel
32 import com.android.quickstep.task.thumbnail.TaskThumbnailView
33 import com.android.quickstep.task.viewmodel.TaskContainerData
34 import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
35 import com.android.systemui.shared.recents.model.Task
36 
37 /** Holder for all Task dependent information. */
38 class TaskContainer(
39     val taskView: TaskView,
40     val task: Task,
41     val snapshotView: View,
42     val iconView: TaskViewIcon,
43     /**
44      * This technically can be a vanilla [android.view.TouchDelegate] class, however that class
45      * requires setting the touch bounds at construction, so we'd repeatedly be created many
46      * instances unnecessarily as scrolling occurs, whereas [TransformingTouchDelegate] allows touch
47      * delegated bounds only to be updated.
48      */
49     val iconTouchDelegate: TransformingTouchDelegate,
50     /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
51     @SplitConfigurationOptions.StagePosition val stagePosition: Int,
52     val digitalWellBeingToast: DigitalWellBeingToast?,
53     val showWindowsView: View?,
54     taskOverlayFactory: TaskOverlayFactory,
55 ) {
56     val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
57     lateinit var taskContainerData: TaskContainerData
58 
59     private val taskThumbnailViewModel: TaskThumbnailViewModel by
60         RecentsDependencies.inject(snapshotView)
61 
62     // TODO(b/335649589): Ideally create and obtain this from DI.
<lambda>null63     private val taskContainerViewModel: TaskContainerViewModel by lazy {
64         TaskContainerViewModel(
65             sysUiStatusNavFlagsUseCase = RecentsDependencies.get(),
66             getThumbnailUseCase = RecentsDependencies.get(),
67             splashAlphaUseCase = RecentsDependencies.get(),
68         )
69     }
70 
71     init {
72         if (enableRefactorTaskThumbnail()) {
73             require(snapshotView is TaskThumbnailView)
74             taskContainerData = RecentsDependencies.get(this)
<lambda>null75             RecentsDependencies.getScope(snapshotView).apply {
76                 val taskViewScope = RecentsDependencies.getScope(taskView)
77                 linkTo(taskViewScope)
78 
79                 val taskContainerScope = RecentsDependencies.getScope(this@TaskContainer)
80                 linkTo(taskContainerScope)
81             }
82         } else {
83             require(snapshotView is TaskThumbnailViewDeprecated)
84         }
85     }
86 
87     val splitAnimationThumbnail: Bitmap?
88         get() =
89             if (enableRefactorTaskThumbnail()) {
90                 taskContainerViewModel.getThumbnail(task.key.id)
91             } else {
92                 thumbnailViewDeprecated.thumbnail
93             }
94 
95     val thumbnailView: TaskThumbnailView
96         get() {
97             require(enableRefactorTaskThumbnail())
98             return snapshotView as TaskThumbnailView
99         }
100 
101     val thumbnailViewDeprecated: TaskThumbnailViewDeprecated
102         get() {
103             require(!enableRefactorTaskThumbnail())
104             return snapshotView as TaskThumbnailViewDeprecated
105         }
106 
107     val shouldShowSplashView: Boolean
108         get() =
109             if (enableRefactorTaskThumbnail())
110                 taskContainerViewModel.shouldShowThumbnailSplash(task.key.id)
111             else thumbnailViewDeprecated.shouldShowSplashView()
112 
113     val sysUiStatusNavFlags: Int
114         get() =
115             if (enableRefactorTaskThumbnail())
116                 taskContainerViewModel.getSysUiStatusNavFlags(task.key.id)
117             else thumbnailViewDeprecated.sysUiStatusNavFlags
118 
119     /** Builds proto for logging */
120     val itemInfo: TaskViewItemInfo
121         get() = TaskViewItemInfo(this)
122 
bindnull123     fun bind() {
124         digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
125         if (enableRefactorTaskThumbnail()) {
126             bindThumbnailView()
127         } else {
128             thumbnailViewDeprecated.bind(task, overlay, taskView)
129         }
130         overlay.init()
131     }
132 
destroynull133     fun destroy() {
134         digitalWellBeingToast?.destroy()
135         snapshotView.scaleX = 1f
136         snapshotView.scaleY = 1f
137         overlay.destroy()
138         if (enableRefactorTaskThumbnail()) {
139             RecentsDependencies.getInstance().removeScope(snapshotView)
140             RecentsDependencies.getInstance().removeScope(this)
141         }
142     }
143 
bindThumbnailViewnull144     fun bindThumbnailView() {
145         taskThumbnailViewModel.bind(task.key.id)
146     }
147 
setOverlayEnablednull148     fun setOverlayEnabled(enabled: Boolean) {
149         if (!enableRefactorTaskThumbnail()) {
150             thumbnailViewDeprecated.setOverlayEnabled(enabled)
151         }
152     }
153 
addChildForAccessibilitynull154     fun addChildForAccessibility(outChildren: ArrayList<View>) {
155         addAccessibleChildToList(iconView.asView(), outChildren)
156         addAccessibleChildToList(snapshotView, outChildren)
157         showWindowsView?.let { addAccessibleChildToList(it, outChildren) }
158         digitalWellBeingToast?.let { addAccessibleChildToList(it, outChildren) }
159         overlay.addChildForAccessibility(outChildren)
160     }
161 }
162