1 /* 2 * Copyright (C) 2016 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 package com.android.launcher3.model; 17 18 import android.util.Log; 19 20 import androidx.annotation.NonNull; 21 import androidx.annotation.Nullable; 22 23 import com.android.launcher3.LauncherAppState; 24 import com.android.launcher3.LauncherModel; 25 import com.android.launcher3.LauncherModel.CallbackTask; 26 import com.android.launcher3.LauncherModel.ModelUpdateTask; 27 import com.android.launcher3.celllayout.CellPosMapper; 28 import com.android.launcher3.model.BgDataModel.Callbacks; 29 import com.android.launcher3.model.BgDataModel.FixedContainerItems; 30 import com.android.launcher3.model.data.AppInfo; 31 import com.android.launcher3.model.data.ItemInfo; 32 import com.android.launcher3.model.data.WorkspaceItemInfo; 33 import com.android.launcher3.util.ComponentKey; 34 import com.android.launcher3.util.PackageUserKey; 35 import com.android.launcher3.widget.model.WidgetsListBaseEntry; 36 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Objects; 43 import java.util.concurrent.Executor; 44 import java.util.function.Predicate; 45 import java.util.stream.Collectors; 46 47 /** 48 * Extension of {@link ModelUpdateTask} with some utility methods 49 */ 50 public abstract class BaseModelUpdateTask implements ModelUpdateTask { 51 52 private static final boolean DEBUG_TASKS = false; 53 private static final String TAG = "BaseModelUpdateTask"; 54 55 // Nullabilities are explicitly omitted here because these are late-init fields, 56 // They will be non-null after init(), which is always the case in enqueueModelUpdateTask(). 57 private LauncherAppState mApp; 58 private LauncherModel mModel; 59 private BgDataModel mDataModel; 60 private AllAppsList mAllAppsList; 61 private Executor mUiExecutor; 62 init(@onNull final LauncherAppState app, @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel, @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor)63 public void init(@NonNull final LauncherAppState app, @NonNull final LauncherModel model, 64 @NonNull final BgDataModel dataModel, @NonNull final AllAppsList allAppsList, 65 @NonNull final Executor uiExecutor) { 66 mApp = app; 67 mModel = model; 68 mDataModel = dataModel; 69 mAllAppsList = allAppsList; 70 mUiExecutor = uiExecutor; 71 } 72 73 @Override run()74 public final void run() { 75 boolean isModelLoaded = Objects.requireNonNull(mModel).isModelLoaded(); 76 if (!isModelLoaded) { 77 if (DEBUG_TASKS) { 78 Log.d(TAG, "Ignoring model task since loader is pending=" + this); 79 } 80 // Loader has not yet run. 81 return; 82 } 83 execute(mApp, mDataModel, mAllAppsList); 84 } 85 86 /** 87 * Execute the actual task. Called on the worker thread. 88 */ execute(@onNull LauncherAppState app, @NonNull BgDataModel dataModel, @NonNull AllAppsList apps)89 public abstract void execute(@NonNull LauncherAppState app, 90 @NonNull BgDataModel dataModel, @NonNull AllAppsList apps); 91 92 /** 93 * Schedules a {@param task} to be executed on the current callbacks. 94 */ scheduleCallbackTask(@onNull final CallbackTask task)95 public final void scheduleCallbackTask(@NonNull final CallbackTask task) { 96 for (final Callbacks cb : mModel.getCallbacks()) { 97 mUiExecutor.execute(() -> task.execute(cb)); 98 } 99 } 100 getModelWriter()101 public ModelWriter getModelWriter() { 102 // Updates from model task, do not deal with icon position in hotseat. Also no need to 103 // verify changes as the ModelTasks always push the changes to callbacks 104 return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, 105 CellPosMapper.DEFAULT, null); 106 } 107 bindUpdatedWorkspaceItems(@onNull final List<WorkspaceItemInfo> allUpdates)108 public void bindUpdatedWorkspaceItems(@NonNull final List<WorkspaceItemInfo> allUpdates) { 109 // Bind workspace items 110 List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream() 111 .filter(info -> info.id != ItemInfo.NO_ID) 112 .collect(Collectors.toList()); 113 if (!workspaceUpdates.isEmpty()) { 114 scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates)); 115 } 116 117 // Bind extra items if any 118 allUpdates.stream() 119 .mapToInt(info -> info.container) 120 .distinct() 121 .mapToObj(mDataModel.extraItems::get) 122 .filter(Objects::nonNull) 123 .forEach(this::bindExtraContainerItems); 124 } 125 bindExtraContainerItems(@onNull final FixedContainerItems item)126 public void bindExtraContainerItems(@NonNull final FixedContainerItems item) { 127 scheduleCallbackTask(c -> c.bindExtraContainerItems(item)); 128 } 129 bindDeepShortcuts(@onNull final BgDataModel dataModel)130 public void bindDeepShortcuts(@NonNull final BgDataModel dataModel) { 131 final HashMap<ComponentKey, Integer> shortcutMapCopy = 132 new HashMap<>(dataModel.deepShortcutMap); 133 scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy)); 134 } 135 bindUpdatedWidgets(@onNull final BgDataModel dataModel)136 public void bindUpdatedWidgets(@NonNull final BgDataModel dataModel) { 137 final ArrayList<WidgetsListBaseEntry> widgets = 138 dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext()); 139 scheduleCallbackTask(c -> c.bindAllWidgets(widgets)); 140 } 141 deleteAndBindComponentsRemoved(final Predicate<ItemInfo> matcher, @Nullable final String reason)142 public void deleteAndBindComponentsRemoved(final Predicate<ItemInfo> matcher, 143 @Nullable final String reason) { 144 getModelWriter().deleteItemsFromDatabase(matcher, reason); 145 146 // Call the components-removed callback 147 scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher)); 148 } 149 bindApplicationsIfNeeded()150 public void bindApplicationsIfNeeded() { 151 if (mAllAppsList.getAndResetChangeFlag()) { 152 AppInfo[] apps = mAllAppsList.copyData(); 153 int flags = mAllAppsList.getFlags(); 154 Map<PackageUserKey, Integer> packageUserKeytoUidMap = Arrays.stream(apps).collect( 155 Collectors.toMap( 156 appInfo -> new PackageUserKey(appInfo.componentName.getPackageName(), 157 appInfo.user), appInfo -> appInfo.uid, (a, b) -> a)); 158 scheduleCallbackTask(c -> c.bindAllApplications(apps, flags, packageUserKeytoUidMap)); 159 } 160 } 161 } 162