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.recents.data
18 
19 import android.os.UserHandle
20 import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
21 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
22 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
23 import com.android.quickstep.util.TaskVisualsChangeListener
24 import com.android.systemui.shared.recents.model.Task
25 import com.android.systemui.shared.recents.model.ThumbnailData
26 import java.util.concurrent.ConcurrentHashMap
27 
28 /** Delegates the checking of task visuals (thumbnails, high res changes, icons) */
29 interface TaskVisualsChangedDelegate :
30     TaskVisualsChangeListener, HighResLoadingStateChangedCallback {
31     /** Registers a callback for visuals relating to icons */
32     fun registerTaskIconChangedCallback(
33         taskKey: Task.TaskKey,
34         taskIconChangedCallback: TaskIconChangedCallback,
35     )
36 
37     /** Unregisters a callback for visuals relating to icons */
38     fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey)
39 
40     /** Registers a callback for visuals relating to thumbnails */
41     fun registerTaskThumbnailChangedCallback(
42         taskKey: Task.TaskKey,
43         taskThumbnailChangedCallback: TaskThumbnailChangedCallback,
44     )
45 
46     /** Unregisters a callback for visuals relating to thumbnails */
47     fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey)
48 
49     /** A callback for task icon changes */
50     interface TaskIconChangedCallback {
51         /** Informs the listener that the task icon has changed */
52         fun onTaskIconChanged()
53     }
54 
55     /** A callback for task thumbnail changes */
56     interface TaskThumbnailChangedCallback {
57         /** Informs the listener that the task thumbnail data has changed to [thumbnailData] */
58         fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?)
59 
60         /** Informs the listener that the default resolution for loading thumbnails has changed */
61         fun onHighResLoadingStateChanged()
62     }
63 }
64 
65 class TaskVisualsChangedDelegateImpl(
66     private val taskVisualsChangeNotifier: TaskVisualsChangeNotifier,
67     private val highResLoadingStateNotifier: HighResLoadingStateNotifier,
68 ) : TaskVisualsChangedDelegate {
69     private val taskIconChangedCallbacks =
70         ConcurrentHashMap<Int, Pair<Task.TaskKey, TaskIconChangedCallback>>()
71     private val taskThumbnailChangedCallbacks =
72         ConcurrentHashMap<Int, Pair<Task.TaskKey, TaskThumbnailChangedCallback>>()
73 
onTaskIconChangednull74     override fun onTaskIconChanged(taskId: Int) {
75         taskIconChangedCallbacks[taskId]?.let { (_, callback) -> callback.onTaskIconChanged() }
76     }
77 
onTaskIconChangednull78     override fun onTaskIconChanged(pkg: String, user: UserHandle) {
79         taskIconChangedCallbacks.values
80             .filter { (taskKey, _) ->
81                 pkg == taskKey.packageName && user.identifier == taskKey.userId
82             }
83             .forEach { (_, callback) -> callback.onTaskIconChanged() }
84     }
85 
onTaskThumbnailChangednull86     override fun onTaskThumbnailChanged(taskId: Int, thumbnailData: ThumbnailData?): Task? {
87         taskThumbnailChangedCallbacks[taskId]?.let { (_, callback) ->
88             callback.onTaskThumbnailChanged(thumbnailData)
89         }
90         return null
91     }
92 
onHighResLoadingStateChangednull93     override fun onHighResLoadingStateChanged(enabled: Boolean) {
94         taskThumbnailChangedCallbacks.values.forEach { (_, callback) ->
95             callback.onHighResLoadingStateChanged()
96         }
97     }
98 
registerTaskIconChangedCallbacknull99     override fun registerTaskIconChangedCallback(
100         taskKey: Task.TaskKey,
101         taskIconChangedCallback: TaskIconChangedCallback,
102     ) {
103         updateCallbacks {
104             taskIconChangedCallbacks[taskKey.id] = taskKey to taskIconChangedCallback
105         }
106     }
107 
unregisterTaskIconChangedCallbacknull108     override fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey) {
109         updateCallbacks { taskIconChangedCallbacks.remove(taskKey.id) }
110     }
111 
registerTaskThumbnailChangedCallbacknull112     override fun registerTaskThumbnailChangedCallback(
113         taskKey: Task.TaskKey,
114         taskThumbnailChangedCallback: TaskThumbnailChangedCallback,
115     ) {
116         updateCallbacks {
117             taskThumbnailChangedCallbacks[taskKey.id] = taskKey to taskThumbnailChangedCallback
118         }
119     }
120 
unregisterTaskThumbnailChangedCallbacknull121     override fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey) {
122         updateCallbacks { taskThumbnailChangedCallbacks.remove(taskKey.id) }
123     }
124 
125     @Synchronized
updateCallbacksnull126     private fun updateCallbacks(callbackModifier: () -> Unit) {
127         val prevHasCallbacks =
128             taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size > 0
129         callbackModifier()
130 
131         val currHasCallbacks =
132             taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size > 0
133 
134         when {
135             prevHasCallbacks && !currHasCallbacks -> {
136                 taskVisualsChangeNotifier.removeThumbnailChangeListener(this)
137                 highResLoadingStateNotifier.removeCallback(this)
138             }
139             !prevHasCallbacks && currHasCallbacks -> {
140                 taskVisualsChangeNotifier.addThumbnailChangeListener(this)
141                 highResLoadingStateNotifier.addCallback(this)
142             }
143         }
144     }
145 }
146