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