blob: 4c0f1ae80241dec161b7a5ab9876acff20e824d4 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.model;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemFactory;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.List;
/**
* Task to add auto-created workspace items.
*/
public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
private static final String LOG = "AddWorkspaceItemsTask";
private final List<Pair<ItemInfo, Object>> mItemList;
private final WorkspaceItemSpaceFinder mItemSpaceFinder;
/**
* @param itemList items to add on the workspace
*/
public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
this(itemList, new WorkspaceItemSpaceFinder());
}
/**
* @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList,
WorkspaceItemSpaceFinder itemSpaceFinder) {
mItemList = itemList;
mItemSpaceFinder = itemSpaceFinder;
}
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
if (mItemList.isEmpty()) {
return;
}
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final IntArray addedWorkspaceScreensFinal = new IntArray();
synchronized (dataModel) {
IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
List<ItemInfo> filteredItems = new ArrayList<>();
for (Pair<ItemInfo, Object> entry : mItemList) {
ItemInfo item = entry.first;
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON,
LOG + " Item already on workspace.");
}
continue;
}
// b/139663018 Short-circuit this logic if the icon is a system app
if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON,
LOG + " Item is a system app.");
}
continue;
}
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
if (item instanceof WorkspaceItemFactory) {
item = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext());
}
}
if (item != null) {
filteredItems.add(item);
}
}
InstallSessionHelper packageInstaller =
InstallSessionHelper.INSTANCE.get(app.getContext());
LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class);
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
int[] coords = mItemSpaceFinder.findSpaceForItem(app, dataModel, workspaceScreens,
addedWorkspaceScreensFinal, item.spanX, item.spanY);
int screenId = coords[0];
ItemInfo itemInfo;
if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo ||
item instanceof LauncherAppWidgetInfo) {
itemInfo = item;
} else if (item instanceof WorkspaceItemFactory) {
itemInfo = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext());
} else {
throw new RuntimeException("Unexpected info type");
}
if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
String packageName = item.getTargetComponent() != null
? item.getTargetComponent().getPackageName() : null;
if (packageName == null) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + " Null packageName.");
}
continue;
}
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
packageName);
if (!packageInstaller.verifySessionInfo(sessionInfo)) {
FileLog.d(LOG, "Item info failed session info verification. "
+ "Skipping : " + workspaceInfo);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + "Failed verification.");
}
continue;
}
List<LauncherActivityInfo> activities = launcherApps
.getActivityList(packageName, item.user);
boolean hasActivity = activities != null && !activities.isEmpty();
if (sessionInfo == null) {
if (!hasActivity) {
// Session was cancelled, do not add.
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + "Session cancelled");
}
continue;
}
} else {
workspaceInfo.setProgressLevel(
(int) (sessionInfo.getProgress() * 100),
PackageInstallInfo.STATUS_INSTALLING);
}
if (hasActivity) {
// App was installed while launcher was in the background,
// or app was already installed for another user.
itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
.makeWorkspaceItem(app.getContext());
if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) {
// We need this additional check here since we treat all auto added
// workspace items as promise icons. At this point we now have the
// correct intent to compare against existing workspace icons.
// Icon already exists on the workspace and should not be auto-added.
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + "shortcutExists");
}
continue;
}
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
app.getIconCache().getTitleAndIcon(wii,
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
}
}
// Add the shortcut to the db
getModelWriter().addItemToDatabase(itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
coords[1], coords[2]);
// Save the WorkspaceItemInfo for binding in the workspace
addedItemsFinal.add(itemInfo);
// log bitmap and label
FileLog.d(LOG, "Adding item info to workspace: " + itemInfo);
}
}
if (!addedItemsFinal.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
int lastScreenId = info.screenId;
for (ItemInfo i : addedItemsFinal) {
if (i.screenId == lastScreenId) {
addAnimated.add(i);
} else {
addNotAnimated.add(i);
}
}
}
callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
addNotAnimated, addAnimated);
}
});
}
}
/**
* Returns true if the shortcuts already exists on the workspace. This must be called after
* the workspace has been loaded. We identify a shortcut by its intent.
*/
protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
final String compPkgName, intentWithPkg, intentWithoutPkg;
if (intent == null) {
// Skip items with null intents
return true;
}
if (intent.getComponent() != null) {
// If component is not null, an intent with null package will produce
// the same result and should also be a match.
compPkgName = intent.getComponent().getPackageName();
if (intent.getPackage() != null) {
intentWithPkg = intent.toUri(0);
intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
} else {
intentWithPkg = new Intent(intent).setPackage(compPkgName).toUri(0);
intentWithoutPkg = intent.toUri(0);
}
} else {
compPkgName = null;
intentWithPkg = intent.toUri(0);
intentWithoutPkg = intent.toUri(0);
}
boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent);
synchronized (dataModel) {
for (ItemInfo item : dataModel.itemsIdMap) {
if (item instanceof WorkspaceItemInfo) {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
if (item.getIntent() != null && info.user.equals(user)) {
Intent copyIntent = new Intent(item.getIntent());
copyIntent.setSourceBounds(intent.getSourceBounds());
String s = copyIntent.toUri(0);
if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
return true;
}
// checking for existing promise icon with same package name
if (isLauncherAppTarget
&& info.isPromise()
&& info.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
&& info.getTargetComponent() != null
&& compPkgName != null
&& compPkgName.equals(info.getTargetComponent().getPackageName())) {
return true;
}
}
}
}
}
return false;
}
}