Files
Lawnchair/src/com/android/launcher3/model/ItemInstallQueue.java
T
Schneider Victor-tulias fff7cef9f1 Remove unused debug logs
Removing debug logs for bugs that have already been closed. These logs are filling up the logcat buffer and impeding in other test investigations.

Flag: not needed
Test: Tapl tests
Bug: 260260325
Change-Id: I4d69b904f2ea79d758cf6bef7d90d172f2eb2a91
2023-04-25 11:00:37 -07:00

393 lines
15 KiB
Java

/*
* Copyright (C) 2008 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 static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PersistedItemArray;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Class to maintain a queue of pending items to be added to the workspace.
*/
public class ItemInstallQueue {
private static final String LOG = "ItemInstallQueue";
public static final int FLAG_ACTIVITY_PAUSED = 1;
public static final int FLAG_LOADER_RUNNING = 2;
public static final int FLAG_DRAG_AND_DROP = 4;
private static final String TAG = "InstallShortcutReceiver";
// The set of shortcuts that are pending install
private static final String APPS_PENDING_INSTALL = "apps_to_install";
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
new MainThreadInitializedObject<>(ItemInstallQueue::new);
private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
new PersistedItemArray<>(APPS_PENDING_INSTALL);
private final Context mContext;
// Determines whether to defer installing shortcuts immediately until
// processAllPendingInstalls() is called.
private int mInstallQueueDisabledFlags = 0;
// Only accessed on worker thread
private List<PendingInstallShortcutInfo> mItems;
private ItemInstallQueue(Context context) {
mContext = context;
}
@WorkerThread
private void ensureQueueLoaded() {
Preconditions.assertWorkerThread();
if (mItems == null) {
mItems = mStorage.read(mContext, this::decode);
}
}
@WorkerThread
private void addToQueue(PendingInstallShortcutInfo info) {
ensureQueueLoaded();
if (!mItems.contains(info)) {
mItems.add(info);
mStorage.write(mContext, mItems);
}
}
@WorkerThread
private void flushQueueInBackground() {
Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
if (launcher == null) {
// Launcher not loaded
return;
}
ensureQueueLoaded();
if (mItems.isEmpty()) {
return;
}
List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
.map(info -> info.getItemInfo(mContext))
.collect(Collectors.toList());
// Add the items and clear queue
if (!installQueue.isEmpty()) {
// add log
launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
mItems.clear();
mStorage.getFile(mContext).delete();
}
/**
* Removes previously added items from the queue.
*/
@WorkerThread
public void removeFromInstallQueue(HashSet<String> packageNames, UserHandle user) {
if (packageNames.isEmpty()) {
return;
}
ensureQueueLoaded();
if (mItems.removeIf(item ->
item.user.equals(user) && packageNames.contains(getIntentPackage(item.intent)))) {
mStorage.write(mContext, mItems);
}
}
/**
* Adds an item to the install queue
*/
public void queueItem(ShortcutInfo info) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info));
}
/**
* Adds an item to the install queue
*/
public void queueItem(AppWidgetProviderInfo info, int widgetId) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId));
}
/**
* Adds an item to the install queue
*/
public void queueItem(String packageName, UserHandle userHandle) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
}
/**
* Returns a stream of all pending shortcuts in the queue
*/
@WorkerThread
public Stream<ShortcutKey> getPendingShortcuts(UserHandle user) {
ensureQueueLoaded();
return mItems.stream()
.filter(item -> item.itemType == ITEM_TYPE_DEEP_SHORTCUT && user.equals(item.user))
.map(item -> ShortcutKey.fromIntent(item.intent, user));
}
private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
final Exception stackTrace = new Exception();
// Queue the item up for adding if launcher has not loaded properly yet
MODEL_EXECUTOR.post(() -> {
Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
if (itemInfo == null) {
FileLog.d(LOG,
"Adding PendingInstallShortcutInfo with no attached info to queue.",
stackTrace);
} else {
FileLog.d(LOG,
"Adding PendingInstallShortcutInfo to queue. Attached info: "
+ itemInfo.first,
stackTrace);
}
addToQueue(info);
});
flushInstallQueue();
}
/**
* Pauses the push-to-model flow until unpaused. All items are held in the queue and
* not added to the model.
*/
public void pauseModelPush(int flag) {
mInstallQueueDisabledFlags |= flag;
}
/**
* Adds all the queue items to the model if the use is completely resumed.
*/
public void resumeModelPush(int flag) {
mInstallQueueDisabledFlags &= ~flag;
flushInstallQueue();
}
private void flushInstallQueue() {
if (mInstallQueueDisabledFlags != 0) {
return;
}
MODEL_EXECUTOR.post(this::flushQueueInBackground);
}
private static class PendingInstallShortcutInfo extends ItemInfo {
final Intent intent;
@Nullable ShortcutInfo shortcutInfo;
@Nullable AppWidgetProviderInfo providerInfo;
/**
* Initializes a PendingInstallShortcutInfo to represent a pending launcher target.
*/
public PendingInstallShortcutInfo(String packageName, UserHandle userHandle) {
itemType = Favorites.ITEM_TYPE_APPLICATION;
intent = new Intent().setPackage(packageName);
user = userHandle;
}
/**
* Initializes a PendingInstallShortcutInfo to represent a deep shortcut.
*/
public PendingInstallShortcutInfo(ShortcutInfo info) {
itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
intent = ShortcutKey.makeIntent(info);
user = info.getUserHandle();
shortcutInfo = info;
}
/**
* Initializes a PendingInstallShortcutInfo to represent an app widget.
*/
public PendingInstallShortcutInfo(AppWidgetProviderInfo info, int widgetId) {
itemType = Favorites.ITEM_TYPE_APPWIDGET;
intent = new Intent()
.setComponent(info.provider)
.putExtra(EXTRA_APPWIDGET_ID, widgetId);
user = info.getProfile();
providerInfo = info;
}
@Override
@Nullable
public Intent getIntent() {
return intent;
}
public Pair<ItemInfo, Object> getItemInfo(Context context) {
switch (itemType) {
case ITEM_TYPE_APPLICATION: {
String packageName = intent.getPackage();
List<LauncherActivityInfo> laiList =
context.getSystemService(LauncherApps.class)
.getActivityList(packageName, user);
final WorkspaceItemInfo si = new WorkspaceItemInfo();
si.user = user;
si.itemType = ITEM_TYPE_APPLICATION;
LauncherActivityInfo lai;
boolean usePackageIcon = laiList.isEmpty();
if (usePackageIcon) {
lai = null;
si.intent = makeLaunchIntent(new ComponentName(packageName, ""))
.setPackage(packageName);
si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
} else {
lai = laiList.get(0);
si.intent = makeLaunchIntent(lai);
}
LauncherAppState.getInstance(context).getIconCache()
.getTitleAndIcon(si, () -> lai, usePackageIcon, false);
return Pair.create(si, null);
}
case ITEM_TYPE_DEEP_SHORTCUT: {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, context);
LauncherAppState.getInstance(context).getIconCache()
.getShortcutIcon(itemInfo, shortcutInfo);
return Pair.create(itemInfo, shortcutInfo);
}
case ITEM_TYPE_APPWIDGET: {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
.fromProviderInfo(context, providerInfo);
LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
info.provider);
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
widgetInfo.minSpanX = info.minSpanX;
widgetInfo.minSpanY = info.minSpanY;
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
widgetInfo.user = user;
return Pair.create(widgetInfo, providerInfo);
}
}
return null;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PendingInstallShortcutInfo) {
PendingInstallShortcutInfo other = (PendingInstallShortcutInfo) obj;
boolean userMatches = user.equals(other.user);
boolean itemTypeMatches = itemType == other.itemType;
boolean intentMatches = intent.toUri(0).equals(other.intent.toUri(0));
boolean shortcutInfoMatches = shortcutInfo == null
? other.shortcutInfo == null
: other.shortcutInfo != null
&& shortcutInfo.getId().equals(other.shortcutInfo.getId())
&& shortcutInfo.getPackage().equals(other.shortcutInfo.getPackage());
boolean providerInfoMatches = providerInfo == null
? other.providerInfo == null
: other.providerInfo != null
&& providerInfo.provider.equals(other.providerInfo.provider);
return userMatches
&& itemTypeMatches
&& intentMatches
&& shortcutInfoMatches
&& providerInfoMatches;
}
return false;
}
}
private static String getIntentPackage(Intent intent) {
return intent.getComponent() == null
? intent.getPackage() : intent.getComponent().getPackageName();
}
private PendingInstallShortcutInfo decode(int itemType, UserHandle user, Intent intent) {
switch (itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
return new PendingInstallShortcutInfo(intent.getPackage(), user);
case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
List<ShortcutInfo> si = ShortcutKey.fromIntent(intent, user)
.buildRequest(mContext)
.query(ShortcutRequest.ALL);
if (si.isEmpty()) {
return null;
} else {
return new PendingInstallShortcutInfo(si.get(0));
}
}
case Favorites.ITEM_TYPE_APPWIDGET: {
int widgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
AppWidgetProviderInfo info =
AppWidgetManager.getInstance(mContext).getAppWidgetInfo(widgetId);
if (info == null || !info.provider.equals(intent.getComponent())
|| !info.getProfile().equals(user)) {
return null;
}
return new PendingInstallShortcutInfo(info, widgetId);
}
default:
Log.e(TAG, "Unknown item type");
}
return null;
}
}