2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.android.launcher3.model;
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.content.pm.ResolveInfo;
23 import android.graphics.Bitmap;
24 import android.os.UserHandle;
25 import android.util.Log;
27 import com.android.launcher3.AllAppsList;
28 import com.android.launcher3.AppInfo;
29 import com.android.launcher3.IconCache;
30 import com.android.launcher3.InstallShortcutReceiver;
31 import com.android.launcher3.ItemInfo;
32 import com.android.launcher3.LauncherAppState;
33 import com.android.launcher3.LauncherAppWidgetInfo;
34 import com.android.launcher3.LauncherModel;
35 import com.android.launcher3.LauncherModel.CallbackTask;
36 import com.android.launcher3.LauncherModel.Callbacks;
37 import com.android.launcher3.LauncherSettings;
38 import com.android.launcher3.LauncherSettings.Favorites;
39 import com.android.launcher3.ShortcutInfo;
40 import com.android.launcher3.Utilities;
41 import com.android.launcher3.compat.LauncherAppsCompat;
42 import com.android.launcher3.compat.UserManagerCompat;
43 import com.android.launcher3.graphics.LauncherIcons;
44 import com.android.launcher3.util.FlagOp;
45 import com.android.launcher3.util.ItemInfoMatcher;
46 import com.android.launcher3.util.ManagedProfileHeuristic;
47 import com.android.launcher3.util.PackageUserKey;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.HashSet;
56 * Handles updates due to changes in package manager (app installed/updated/removed)
57 * or when a user availability changes.
59 public class PackageUpdatedTask extends ExtendedModelTask {
61 private static final boolean DEBUG = false;
62 private static final String TAG = "PackageUpdatedTask";
64 public static final int OP_NONE = 0;
65 public static final int OP_ADD = 1;
66 public static final int OP_UPDATE = 2;
67 public static final int OP_REMOVE = 3; // uninstalled
68 public static final int OP_UNAVAILABLE = 4; // external media unmounted
69 public static final int OP_SUSPEND = 5; // package suspended
70 public static final int OP_UNSUSPEND = 6; // package unsuspended
71 public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
73 private final int mOp;
74 private final UserHandle mUser;
75 private final String[] mPackages;
77 public PackageUpdatedTask(int op, UserHandle user, String... packages) {
84 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
85 final Context context = app.getContext();
86 final IconCache iconCache = app.getIconCache();
88 final String[] packages = mPackages;
89 final int N = packages.length;
90 FlagOp flagOp = FlagOp.NO_OP;
91 final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
92 ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
95 for (int i = 0; i < N; i++) {
96 if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
97 iconCache.updateIconsForPkg(packages[i], mUser);
98 appsList.addPackage(context, packages[i], mUser);
101 ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
102 if (heuristic != null) {
103 heuristic.processPackageAdd(mPackages);
108 for (int i = 0; i < N; i++) {
109 if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
110 iconCache.updateIconsForPkg(packages[i], mUser);
111 appsList.updatePackage(context, packages[i], mUser);
112 app.getWidgetCache().removePackage(packages[i], mUser);
114 // Since package was just updated, the target must be available now.
115 flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
118 ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
119 if (heuristic != null) {
120 heuristic.processPackageRemoved(mPackages);
122 for (int i = 0; i < N; i++) {
123 iconCache.removeIconsForPkg(packages[i], mUser);
128 for (int i = 0; i < N; i++) {
129 if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
130 appsList.removePackage(packages[i], mUser);
131 app.getWidgetCache().removePackage(packages[i], mUser);
133 flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
137 flagOp = mOp == OP_SUSPEND ?
138 FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
139 FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
140 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
141 appsList.updateDisabledFlags(matcher, flagOp);
143 case OP_USER_AVAILABILITY_CHANGE:
144 flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
145 ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
146 : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
147 // We want to update all packages for this user.
148 matcher = ItemInfoMatcher.ofUser(mUser);
149 appsList.updateDisabledFlags(matcher, flagOp);
153 ArrayList<AppInfo> added = null;
154 ArrayList<AppInfo> modified = null;
155 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
157 if (appsList.added.size() > 0) {
158 added = new ArrayList<>(appsList.added);
159 appsList.added.clear();
161 if (appsList.modified.size() > 0) {
162 modified = new ArrayList<>(appsList.modified);
163 appsList.modified.clear();
165 if (appsList.removed.size() > 0) {
166 removedApps.addAll(appsList.removed);
167 appsList.removed.clear();
170 final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
173 final ArrayList<AppInfo> addedApps = added;
174 scheduleCallbackTask(new CallbackTask() {
176 public void execute(Callbacks callbacks) {
177 callbacks.bindAppsAdded(null, null, null, addedApps);
180 for (AppInfo ai : added) {
181 addedOrUpdatedApps.put(ai.componentName, ai);
185 if (modified != null) {
186 final ArrayList<AppInfo> modifiedFinal = modified;
187 for (AppInfo ai : modified) {
188 addedOrUpdatedApps.put(ai.componentName, ai);
190 scheduleCallbackTask(new CallbackTask() {
192 public void execute(Callbacks callbacks) {
193 callbacks.bindAppsUpdated(modifiedFinal);
198 // Update shortcut infos
199 if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
200 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
201 final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<>();
202 final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
204 synchronized (dataModel) {
205 for (ItemInfo info : dataModel.itemsIdMap) {
206 if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
207 ShortcutInfo si = (ShortcutInfo) info;
208 boolean infoUpdated = false;
209 boolean shortcutUpdated = false;
211 // Update shortcuts which use iconResource.
212 if ((si.iconResource != null)
213 && packageSet.contains(si.iconResource.packageName)) {
214 Bitmap icon = LauncherIcons.createIconBitmap(si.iconResource, context);
216 si.iconBitmap = icon;
221 ComponentName cn = si.getTargetComponent();
222 if (cn != null && matcher.matches(si, cn)) {
223 AppInfo appInfo = addedOrUpdatedApps.get(cn);
225 if (si.isPromise() && mOp == OP_ADD) {
226 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
228 PackageManager pm = context.getPackageManager();
229 ResolveInfo matched = pm.resolveActivity(
230 new Intent(Intent.ACTION_MAIN)
231 .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
232 PackageManager.MATCH_DEFAULT_ONLY);
233 if (matched == null) {
234 // Try to find the best match activity.
235 Intent intent = pm.getLaunchIntentForPackage(
236 cn.getPackageName());
237 if (intent != null) {
238 cn = intent.getComponent();
239 appInfo = addedOrUpdatedApps.get(cn);
242 if ((intent == null) || (appInfo == null)) {
243 removedShortcuts.add(si);
250 si.status = ShortcutInfo.DEFAULT;
252 if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
253 iconCache.getTitleAndIcon(si, si.usingLowResIcon);
257 if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
258 && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
259 iconCache.getTitleAndIcon(si, si.usingLowResIcon);
263 int oldDisabledFlags = si.isDisabled;
264 si.isDisabled = flagOp.apply(si.isDisabled);
265 if (si.isDisabled != oldDisabledFlags) {
266 shortcutUpdated = true;
270 if (infoUpdated || shortcutUpdated) {
271 updatedShortcuts.add(si);
274 getModelWriter().updateItemInDatabase(si);
276 } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
277 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
278 if (mUser.equals(widgetInfo.user)
279 && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
280 && packageSet.contains(widgetInfo.providerName.getPackageName())) {
281 widgetInfo.restoreStatus &=
282 ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
283 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
285 // adding this flag ensures that launcher shows 'click to setup'
286 // if the widget has a config activity. In case there is no config
287 // activity, it will be marked as 'restored' during bind.
288 widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
290 widgets.add(widgetInfo);
291 getModelWriter().updateItemInDatabase(widgetInfo);
297 bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
298 if (!removedShortcuts.isEmpty()) {
299 getModelWriter().deleteItemsFromDatabase(removedShortcuts);
302 if (!widgets.isEmpty()) {
303 scheduleCallbackTask(new CallbackTask() {
305 public void execute(Callbacks callbacks) {
306 callbacks.bindWidgetsRestored(widgets);
312 final HashSet<String> removedPackages = new HashSet<>();
313 final HashSet<ComponentName> removedComponents = new HashSet<>();
314 if (mOp == OP_REMOVE) {
315 // Mark all packages in the broadcast to be removed
316 Collections.addAll(removedPackages, packages);
318 // No need to update the removedComponents as
319 // removedPackages is a super-set of removedComponents
320 } else if (mOp == OP_UPDATE) {
321 // Mark disabled packages in the broadcast to be removed
322 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
323 for (int i=0; i<N; i++) {
324 if (!launcherApps.isPackageEnabledForProfile(packages[i], mUser)) {
325 removedPackages.add(packages[i]);
329 // Update removedComponents as some components can get removed during package update
330 for (AppInfo info : removedApps) {
331 removedComponents.add(info.componentName);
335 if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
336 getModelWriter().deleteItemsFromDatabase(
337 ItemInfoMatcher.ofPackages(removedPackages, mUser));
338 getModelWriter().deleteItemsFromDatabase(
339 ItemInfoMatcher.ofComponents(removedComponents, mUser));
341 // Remove any queued items from the install queue
342 InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
344 // Call the components-removed callback
345 scheduleCallbackTask(new CallbackTask() {
347 public void execute(Callbacks callbacks) {
348 callbacks.bindWorkspaceComponentsRemoved(
349 removedPackages, removedComponents, mUser);
354 if (!removedApps.isEmpty()) {
355 // Remove corresponding apps from All-Apps
356 scheduleCallbackTask(new CallbackTask() {
358 public void execute(Callbacks callbacks) {
359 callbacks.bindAppInfosRemoved(removedApps);
364 // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
365 // get widget update signals.
366 if (!Utilities.ATLEAST_MARSHMALLOW &&
367 (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
368 scheduleCallbackTask(new CallbackTask() {
370 public void execute(Callbacks callbacks) {
371 callbacks.notifyWidgetProvidersChanged();
374 } else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
375 // Load widgets for the new package.
376 for (int i = 0; i < N; i++) {
377 LauncherModel model = app.getModel();
378 model.refreshAndBindWidgetsAndShortcuts(
379 model.getCallback(), false /* bindFirst */,
380 new PackageUserKey(packages[i], mUser) /* packageUser */);