mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-17 09:38:19 +00:00
> Addition/removal of shortcus is already removed. This just cleans up the unused code path Bug: 275875209 Test: Updated tests Flag: N/A Change-Id: I8ab7f57b693f996920e50e8beecafcffab5167e9
392 lines
15 KiB
Java
392 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;
|
|
|
|
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;
|
|
}
|
|
}
|