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.launcher3.model
18 
19 import com.android.launcher3.LauncherAppState
20 import com.android.launcher3.LauncherModel
21 import com.android.launcher3.LauncherModel.CallbackTask
22 import com.android.launcher3.celllayout.CellPosMapper
23 import com.android.launcher3.model.BgDataModel.FixedContainerItems
24 import com.android.launcher3.model.data.ItemInfo
25 import com.android.launcher3.model.data.WorkspaceItemInfo
26 import com.android.launcher3.util.PackageUserKey
27 import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder
28 import java.util.Objects
29 import java.util.concurrent.Executor
30 import java.util.function.Predicate
31 
32 /** Class with utility methods and properties for running a LauncherModel Task */
33 class ModelTaskController(
34     val app: LauncherAppState,
35     val dataModel: BgDataModel,
36     val allAppsList: AllAppsList,
37     private val model: LauncherModel,
38     private val uiExecutor: Executor,
39 ) {
40 
41     /** Schedules a {@param task} to be executed on the current callbacks. */
42     fun scheduleCallbackTask(task: CallbackTask) {
43         for (cb in model.callbacks) {
44             uiExecutor.execute { task.execute(cb) }
45         }
46     }
47 
48     /**
49      * Updates from model task, do not deal with icon position in hotseat. Also no need to verify
50      * changes as the ModelTasks always push the changes to callbacks
51      */
52     fun getModelWriter() = model.getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null)
53 
54     fun bindUpdatedWorkspaceItems(allUpdates: List<WorkspaceItemInfo>) {
55         // Bind workspace items
56         val workspaceUpdates =
57             allUpdates.stream().filter { info -> info.id != ItemInfo.NO_ID }.toList()
58         if (workspaceUpdates.isNotEmpty()) {
59             scheduleCallbackTask { it.bindWorkspaceItemsChanged(workspaceUpdates) }
60         }
61 
62         // Bind extra items if any
63         allUpdates
64             .stream()
65             .mapToInt { info: WorkspaceItemInfo -> info.container }
66             .distinct()
67             .mapToObj { dataModel.extraItems.get(it) }
68             .filter { Objects.nonNull(it) }
69             .forEach { bindExtraContainerItems(it) }
70     }
71 
72     fun bindExtraContainerItems(item: FixedContainerItems) {
73         scheduleCallbackTask { it.bindExtraContainerItems(item) }
74     }
75 
76     fun bindDeepShortcuts(dataModel: BgDataModel) {
77         val shortcutMapCopy = HashMap(dataModel.deepShortcutMap)
78         scheduleCallbackTask { it.bindDeepShortcutMap(shortcutMapCopy) }
79     }
80 
81     fun bindUpdatedWidgets(dataModel: BgDataModel) {
82         val widgetsByPackageItem = dataModel.widgetsModel.widgetsByPackageItem
83         val allWidgets = WidgetsListBaseEntriesBuilder(app.context).build(widgetsByPackageItem)
84 
85         val defaultWidgetsFilter = dataModel.widgetsModel.defaultWidgetsFilter
86         val defaultWidgets =
87             if (defaultWidgetsFilter != null) {
88                 WidgetsListBaseEntriesBuilder(app.context)
89                     .build(widgetsByPackageItem, defaultWidgetsFilter)
90             } else {
91                 emptyList()
92             }
93 
94         scheduleCallbackTask { it.bindAllWidgets(allWidgets, defaultWidgets) }
95     }
96 
97     fun deleteAndBindComponentsRemoved(matcher: Predicate<ItemInfo?>, reason: String?) {
98         getModelWriter().deleteItemsFromDatabase(matcher, reason)
99 
100         // Call the components-removed callback
101         scheduleCallbackTask { it.bindWorkspaceComponentsRemoved(matcher) }
102     }
103 
104     fun bindApplicationsIfNeeded() {
105         if (allAppsList.getAndResetChangeFlag()) {
106             val apps = allAppsList.copyData()
107             val flags = allAppsList.flags
108             val packageUserKeyToUidMap =
109                 apps.associateBy(
110                     keySelector = { PackageUserKey(it.componentName!!.packageName, it.user) },
111                     valueTransform = { it.uid },
112                 )
113             scheduleCallbackTask { it.bindAllApplications(apps, flags, packageUserKeyToUidMap) }
114         }
115     }
116 }
117