Merge \"Add support for launcher shortcuts.\" into ub-launcher3-calgary
am: ec1b1c542c
Change-Id: I1993ee5db57bdb300430ef5b106c4c95d6f50a0c
This commit is contained in:
+1
-1
@@ -18,7 +18,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "com.android.launcher3"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 23
|
||||
targetSdkVersion 'N'
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ public class BubbleTextView extends TextView
|
||||
if (info.isDisabled()) {
|
||||
iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
|
||||
}
|
||||
setIcon(iconDrawable, mIconSize);
|
||||
setIcon(iconDrawable);
|
||||
if (info.contentDescription != null) {
|
||||
setContentDescription(info.contentDescription);
|
||||
}
|
||||
@@ -175,7 +175,7 @@ public class BubbleTextView extends TextView
|
||||
if (info.isDisabled()) {
|
||||
iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
|
||||
}
|
||||
setIcon(iconDrawable, mIconSize);
|
||||
setIcon(iconDrawable);
|
||||
setText(info.title);
|
||||
if (info.contentDescription != null) {
|
||||
setContentDescription(info.contentDescription);
|
||||
@@ -188,7 +188,7 @@ public class BubbleTextView extends TextView
|
||||
}
|
||||
|
||||
public void applyFromPackageItemInfo(PackageItemInfo info) {
|
||||
setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
|
||||
setIcon(mLauncher.createIconDrawable(info.iconBitmap));
|
||||
setText(info.title);
|
||||
if (info.contentDescription != null) {
|
||||
setContentDescription(info.contentDescription);
|
||||
@@ -205,7 +205,7 @@ public class BubbleTextView extends TextView
|
||||
*/
|
||||
public void applyDummyInfo() {
|
||||
ColorDrawable d = new ColorDrawable();
|
||||
setIcon(mLauncher.resizeIconDrawable(d), mIconSize);
|
||||
setIcon(mLauncher.resizeIconDrawable(d));
|
||||
setText("");
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ public class BubbleTextView extends TextView
|
||||
preloadDrawable = (PreloadIconDrawable) mIcon;
|
||||
} else {
|
||||
preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme());
|
||||
setIcon(preloadDrawable, mIconSize);
|
||||
setIcon(preloadDrawable);
|
||||
}
|
||||
|
||||
preloadDrawable.setLevel(progressLevel);
|
||||
@@ -506,10 +506,10 @@ public class BubbleTextView extends TextView
|
||||
* Sets the icon for this view based on the layout direction.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
private Drawable setIcon(Drawable icon, int iconSize) {
|
||||
public void setIcon(Drawable icon) {
|
||||
mIcon = icon;
|
||||
if (iconSize != -1) {
|
||||
mIcon.setBounds(0, 0, iconSize, iconSize);
|
||||
if (mIconSize != -1) {
|
||||
mIcon.setBounds(0, 0, mIconSize, mIconSize);
|
||||
}
|
||||
if (mLayoutHorizontal) {
|
||||
if (Utilities.ATLEAST_JB_MR1) {
|
||||
@@ -520,7 +520,6 @@ public class BubbleTextView extends TextView
|
||||
} else {
|
||||
setCompoundDrawables(null, mIcon, null, null);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ItemInfo {
|
||||
/**
|
||||
* Intent extra to store the profile. Format: UserHandle
|
||||
*/
|
||||
static final String EXTRA_PROFILE = "profile";
|
||||
public static final String EXTRA_PROFILE = "profile";
|
||||
|
||||
public static final int NO_ID = -1;
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.pageindicators.PageIndicator;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.TestingUtils;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
@@ -293,6 +294,9 @@ public class Launcher extends Activity
|
||||
private boolean mHasFocus = false;
|
||||
private boolean mAttached = false;
|
||||
|
||||
/** Maps launcher activity components to their list of shortcut ids. */
|
||||
private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
|
||||
|
||||
private LauncherClings mClings;
|
||||
|
||||
private View.OnTouchListener mHapticFeedbackTouchListener;
|
||||
@@ -2333,7 +2337,7 @@ public class Launcher extends Activity
|
||||
* @param itemInfo the {@link ItemInfo} for this view.
|
||||
* @param deleteFromDb whether or not to delete this item from the db.
|
||||
*/
|
||||
public boolean removeItem(View v, ItemInfo itemInfo, boolean deleteFromDb) {
|
||||
public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
|
||||
if (itemInfo instanceof ShortcutInfo) {
|
||||
// Remove the shortcut from the folder before removing it from launcher
|
||||
View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
|
||||
@@ -2361,7 +2365,6 @@ public class Launcher extends Activity
|
||||
if (deleteFromDb) {
|
||||
deleteWidgetInfo(widgetInfo);
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -2798,8 +2801,16 @@ public class Launcher extends Activity
|
||||
// is enabled by default on NYC.
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
|
||||
.penaltyLog().build());
|
||||
// Could be launching some bookkeeping activity
|
||||
startActivity(intent, optsBundle);
|
||||
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
String id = ((ShortcutInfo) info).getDeepShortcutId();
|
||||
String packageName = intent.getPackage();
|
||||
LauncherAppsCompat.getInstance(this).startShortcut(
|
||||
packageName, id, intent.getSourceBounds(), optsBundle, info.user);
|
||||
} else {
|
||||
// Could be launching some bookkeeping activity
|
||||
startActivity(intent, optsBundle);
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setVmPolicy(oldPolicy);
|
||||
}
|
||||
@@ -2875,8 +2886,9 @@ public class Launcher extends Activity
|
||||
new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()));
|
||||
}
|
||||
try {
|
||||
if (Utilities.ATLEAST_MARSHMALLOW &&
|
||||
item != null && item.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
|
||||
if (Utilities.ATLEAST_MARSHMALLOW && item != null
|
||||
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
|
||||
// Shortcuts need some special checks due to legacy reasons.
|
||||
startShortcutIntentSafely(intent, optsBundle, item);
|
||||
} else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
|
||||
@@ -3688,6 +3700,7 @@ public class Launcher extends Activity
|
||||
switch (item.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
ShortcutInfo info = (ShortcutInfo) item;
|
||||
view = createShortcut(info);
|
||||
break;
|
||||
@@ -4043,6 +4056,16 @@ public class Launcher extends Activity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
|
||||
* because LauncherModel's map is updated in the background, while Launcher runs on the UI.
|
||||
*/
|
||||
@Override
|
||||
public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
|
||||
mDeepShortcutMap = deepShortcutMapCopy;
|
||||
if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* A package was updated.
|
||||
*
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.launcher3.config.ProviderConfig;
|
||||
import com.android.launcher3.dynamicui.ExtractionUtils;
|
||||
import com.android.launcher3.logging.FileLog;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.ShortcutCache;
|
||||
import com.android.launcher3.util.ConfigMonitor;
|
||||
import com.android.launcher3.util.TestingUtils;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
@@ -42,6 +44,7 @@ public class LauncherAppState {
|
||||
@Thunk final LauncherModel mModel;
|
||||
private final IconCache mIconCache;
|
||||
private final WidgetPreviewLoader mWidgetCache;
|
||||
private final DeepShortcutManager mDeepShortcutManager;
|
||||
|
||||
@Thunk boolean mWallpaperChangedSinceLastCheck;
|
||||
|
||||
@@ -95,9 +98,10 @@ public class LauncherAppState {
|
||||
mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
|
||||
mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
|
||||
mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
|
||||
mDeepShortcutManager = new DeepShortcutManager(sContext, new ShortcutCache());
|
||||
|
||||
mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
|
||||
mModel = new LauncherModel(this, mIconCache, mAppFilter);
|
||||
mModel = new LauncherModel(this, mIconCache, mAppFilter, mDeepShortcutManager);
|
||||
|
||||
LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
|
||||
|
||||
@@ -168,6 +172,10 @@ public class LauncherAppState {
|
||||
return mWidgetCache;
|
||||
}
|
||||
|
||||
public DeepShortcutManager getShortcutManager() {
|
||||
return mDeepShortcutManager;
|
||||
}
|
||||
|
||||
public boolean hasWallpaperChangedSinceLastCheck() {
|
||||
boolean result = mWallpaperChangedSinceLastCheck;
|
||||
mWallpaperChangedSinceLastCheck = false;
|
||||
|
||||
@@ -560,7 +560,8 @@ public class LauncherBackupHelper implements BackupHelper {
|
||||
|
||||
// Don't backup apps in other profiles for now.
|
||||
String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
|
||||
Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " +
|
||||
Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + " OR " +
|
||||
Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_DEEP_SHORTCUT + ") AND " +
|
||||
getUserSelectionArg();
|
||||
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
|
||||
where, null, null);
|
||||
@@ -798,7 +799,8 @@ public class LauncherBackupHelper implements BackupHelper {
|
||||
return favorite.container == Favorites.CONTAINER_HOTSEAT
|
||||
&& favorite.intent != null
|
||||
&& (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION
|
||||
|| favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT);
|
||||
|| favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT);
|
||||
}
|
||||
|
||||
/** Serialize a Favorite for persistence, including a checksum wrapper. */
|
||||
@@ -835,7 +837,8 @@ public class LauncherBackupHelper implements BackupHelper {
|
||||
if (!TextUtils.isEmpty(appWidgetProvider)) {
|
||||
favorite.appWidgetProvider = appWidgetProvider;
|
||||
}
|
||||
} else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
|
||||
} else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
String iconPackage = c.getString(ICON_PACKAGE_INDEX);
|
||||
String iconResource = c.getString(ICON_RESOURCE_INDEX);
|
||||
if (!TextUtils.isEmpty(iconPackage) && !TextUtils.isEmpty(iconResource)) {
|
||||
@@ -897,7 +900,8 @@ public class LauncherBackupHelper implements BackupHelper {
|
||||
values.put(Favorites.SPANY, favorite.spanY);
|
||||
values.put(Favorites.RANK, favorite.rank);
|
||||
|
||||
if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
|
||||
if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
values.put(Favorites.ICON_PACKAGE, favorite.iconPackage);
|
||||
values.put(Favorites.ICON_RESOURCE, favorite.iconResource);
|
||||
values.put(Favorites.ICON, favorite.icon);
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.provider.BaseColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.MutableInt;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
@@ -60,6 +61,9 @@ import com.android.launcher3.logging.FileLog;
|
||||
import com.android.launcher3.model.GridSizeMigrationTask;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.provider.LauncherDbUtils;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.CursorIconInfo;
|
||||
import com.android.launcher3.util.FlagOp;
|
||||
@@ -69,6 +73,7 @@ import com.android.launcher3.util.ManagedProfileHeuristic;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.StringFilter;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||
|
||||
@@ -83,6 +88,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -119,8 +125,9 @@ public class LauncherModel extends BroadcastReceiver
|
||||
// We start off with everything not loaded. After that, we assume that
|
||||
// our monitoring of the package manager provides all updates and we never
|
||||
// need to do a requery. These are only ever touched from the loader thread.
|
||||
@Thunk boolean mWorkspaceLoaded;
|
||||
@Thunk boolean mAllAppsLoaded;
|
||||
private boolean mWorkspaceLoaded;
|
||||
private boolean mAllAppsLoaded;
|
||||
private boolean mDeepShortcutsLoaded;
|
||||
|
||||
/**
|
||||
* Set of runnables to be called on the background thread after the workspace binding
|
||||
@@ -135,6 +142,9 @@ public class LauncherModel extends BroadcastReceiver
|
||||
// Entire list of widgets.
|
||||
private final WidgetsModel mBgWidgetsModel;
|
||||
|
||||
// Maps all launcher activities to the id's of their shortcuts (if they have any).
|
||||
private final MultiHashMap<ComponentKey, String> mBgDeepShortcutMap = new MultiHashMap<>();
|
||||
|
||||
// The lock that must be acquired before referencing any static bg data structures. Unlike
|
||||
// other locks, this one can generally be held long-term because we never expect any of these
|
||||
// static data structures to be referenced outside of the worker thread except on the first
|
||||
@@ -160,16 +170,21 @@ public class LauncherModel extends BroadcastReceiver
|
||||
// sBgWorkspaceScreens is the ordered set of workspace screens.
|
||||
static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
|
||||
|
||||
// sBgPinnedShortcutCounts is the ComponentKey representing a pinned shortcut to the number of
|
||||
// times it is pinned.
|
||||
static final Map<ShortcutKey, MutableInt> sBgPinnedShortcutCounts = new HashMap<>();
|
||||
|
||||
// sPendingPackages is a set of packages which could be on sdcard and are not available yet
|
||||
static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
|
||||
new HashMap<UserHandleCompat, HashSet<String>>();
|
||||
|
||||
// </ only access in worker thread >
|
||||
|
||||
@Thunk IconCache mIconCache;
|
||||
private IconCache mIconCache;
|
||||
private DeepShortcutManager mDeepShortcutManager;
|
||||
|
||||
@Thunk final LauncherAppsCompat mLauncherApps;
|
||||
@Thunk final UserManagerCompat mUserManager;
|
||||
private final LauncherAppsCompat mLauncherApps;
|
||||
private final UserManagerCompat mUserManager;
|
||||
|
||||
public interface Callbacks {
|
||||
public boolean setLoadOnResume();
|
||||
@@ -199,18 +214,21 @@ public class LauncherModel extends BroadcastReceiver
|
||||
public void bindWidgetsModel(WidgetsModel model);
|
||||
public void onPageBoundSynchronously(int page);
|
||||
public void executeOnNextDraw(ViewOnDrawExecutor executor);
|
||||
public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
|
||||
}
|
||||
|
||||
public interface ItemInfoFilter {
|
||||
public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
|
||||
}
|
||||
|
||||
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
|
||||
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter,
|
||||
DeepShortcutManager deepShortcutManager) {
|
||||
Context context = app.getContext();
|
||||
mApp = app;
|
||||
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
|
||||
mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
|
||||
mIconCache = iconCache;
|
||||
mDeepShortcutManager = deepShortcutManager;
|
||||
|
||||
mLauncherApps = LauncherAppsCompat.getInstance(context);
|
||||
mUserManager = UserManagerCompat.getInstance(context);
|
||||
@@ -679,6 +697,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
switch (modelItem.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
|
||||
if (!sBgWorkspaceItems.contains(modelItem)) {
|
||||
sBgWorkspaceItems.add(modelItem);
|
||||
@@ -892,6 +911,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
// Fall through
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
|
||||
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
||||
sBgWorkspaceItems.add(item);
|
||||
@@ -903,6 +923,14 @@ public class LauncherModel extends BroadcastReceiver
|
||||
Log.e(TAG, msg);
|
||||
}
|
||||
}
|
||||
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
ShortcutInfo shortcutInfo = (ShortcutInfo) item;
|
||||
ShortcutKey shortcutToPin = new ShortcutKey(
|
||||
shortcutInfo.intent.getPackage(),
|
||||
shortcutInfo.user,
|
||||
shortcutInfo.getDeepShortcutId());
|
||||
incrementPinnedShortcutCount(shortcutToPin, true /* shouldPin */);
|
||||
}
|
||||
break;
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
||||
sBgAppWidgets.add((LauncherAppWidgetInfo) item);
|
||||
@@ -969,6 +997,14 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
sBgWorkspaceItems.remove(item);
|
||||
break;
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
ShortcutInfo shortcutInfo = ((ShortcutInfo) item);
|
||||
ShortcutKey pinnedShortcut = new ShortcutKey(
|
||||
shortcutInfo.intent.getPackage(),
|
||||
shortcutInfo.user,
|
||||
shortcutInfo.getDeepShortcutId());
|
||||
decrementPinnedShortcutCount(pinnedShortcut);
|
||||
// Fall through.
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
sBgWorkspaceItems.remove(item);
|
||||
@@ -985,6 +1021,39 @@ public class LauncherModel extends BroadcastReceiver
|
||||
runOnWorkerThread(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the count for the given pinned shortcut, unpinning it if the count becomes 0.
|
||||
*/
|
||||
private static void decrementPinnedShortcutCount(final ShortcutKey pinnedShortcut) {
|
||||
synchronized (sBgLock) {
|
||||
MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
|
||||
if (count == null || --count.value == 0) {
|
||||
LauncherAppState.getInstance().getShortcutManager().unpinShortcut(pinnedShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the count for the given shortcut, pinning it if the count becomes 1.
|
||||
*
|
||||
* As an optimization, the caller can pass shouldPin == false to avoid
|
||||
* unnecessary RPC's if the shortcut is already pinned.
|
||||
*/
|
||||
private static void incrementPinnedShortcutCount(ShortcutKey pinnedShortcut, boolean shouldPin) {
|
||||
synchronized (sBgLock) {
|
||||
MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
|
||||
if (count == null) {
|
||||
count = new MutableInt(1);
|
||||
sBgPinnedShortcutCounts.put(pinnedShortcut, count);
|
||||
} else {
|
||||
count.value++;
|
||||
}
|
||||
if (shouldPin && count.value == 1) {
|
||||
LauncherAppState.getInstance().getShortcutManager().pinShortcut(pinnedShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the order of the workspace screens in the database. The array list contains
|
||||
* a list of screen ids in the order that they should appear.
|
||||
@@ -1077,28 +1146,28 @@ public class LauncherModel extends BroadcastReceiver
|
||||
@Override
|
||||
public void onPackageChanged(String packageName, UserHandleCompat user) {
|
||||
int op = PackageUpdatedTask.OP_UPDATE;
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, UserHandleCompat user) {
|
||||
int op = PackageUpdatedTask.OP_REMOVE;
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageAdded(String packageName, UserHandleCompat user) {
|
||||
int op = PackageUpdatedTask.OP_ADD;
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
|
||||
user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
|
||||
boolean replacing) {
|
||||
enqueuePackageUpdated(
|
||||
enqueueItemUpdatedTask(
|
||||
new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
|
||||
}
|
||||
|
||||
@@ -1106,7 +1175,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
|
||||
boolean replacing) {
|
||||
if (!replacing) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(
|
||||
PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
|
||||
user));
|
||||
}
|
||||
@@ -1114,18 +1183,24 @@ public class LauncherModel extends BroadcastReceiver
|
||||
|
||||
@Override
|
||||
public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(
|
||||
PackageUpdatedTask.OP_SUSPEND, packageNames,
|
||||
user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(
|
||||
PackageUpdatedTask.OP_UNSUSPEND, packageNames,
|
||||
user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
|
||||
UserHandleCompat user) {
|
||||
enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
|
||||
* ACTION_PACKAGE_CHANGED.
|
||||
@@ -1146,7 +1221,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
|
||||
UserHandleCompat user = UserHandleCompat.fromIntent(intent);
|
||||
if (user != null) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(
|
||||
PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
|
||||
new String[0], user));
|
||||
}
|
||||
@@ -1171,6 +1246,8 @@ public class LauncherModel extends BroadcastReceiver
|
||||
stopLoaderLocked();
|
||||
if (resetAllAppsLoaded) mAllAppsLoaded = false;
|
||||
if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
|
||||
// Always reset deep shortcuts loaded.
|
||||
mDeepShortcutsLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,6 +1334,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
* - workspace icons
|
||||
* - widgets
|
||||
* - all apps icons
|
||||
* - deep shortcuts within apps
|
||||
*/
|
||||
private class LoaderTask implements Runnable {
|
||||
private Context mContext;
|
||||
@@ -1388,6 +1466,12 @@ public class LauncherModel extends BroadcastReceiver
|
||||
// second step
|
||||
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
|
||||
loadAndBindAllApps();
|
||||
|
||||
waitForIdle();
|
||||
|
||||
// third step
|
||||
if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
|
||||
loadAndBindDeepShortcuts();
|
||||
}
|
||||
|
||||
// Clear out this reference, otherwise we end up holding it until all of the
|
||||
@@ -1539,6 +1623,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
sBgFolders.clear();
|
||||
sBgItemsIdMap.clear();
|
||||
sBgWorkspaceScreens.clear();
|
||||
sBgPinnedShortcutCounts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1585,6 +1670,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
|
||||
final ArrayList<Long> itemsToRemove = new ArrayList<>();
|
||||
final ArrayList<Long> restoredRows = new ArrayList<>();
|
||||
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
|
||||
final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
|
||||
if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
|
||||
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
|
||||
@@ -1635,6 +1721,13 @@ public class LauncherModel extends BroadcastReceiver
|
||||
long serialNo = mUserManager.getSerialNumberForUser(user);
|
||||
allUsers.put(serialNo, user);
|
||||
quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
|
||||
|
||||
List<ShortcutInfoCompat> pinnedShortcuts = mDeepShortcutManager
|
||||
.queryForPinnedShortcuts(null, user);
|
||||
for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
|
||||
shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
|
||||
shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutInfo info;
|
||||
@@ -1657,6 +1750,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
switch (itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
id = c.getLong(idIndex);
|
||||
intentDescription = c.getString(intentIndex);
|
||||
serialNumber = c.getInt(profileIdIndex);
|
||||
@@ -1819,7 +1913,37 @@ public class LauncherModel extends BroadcastReceiver
|
||||
info = getAppShortcutInfo(intent, user, context, c,
|
||||
cursorIconInfo.iconIndex, titleIndex,
|
||||
allowMissingTarget, useLowResIcon);
|
||||
} else {
|
||||
} else if (itemType ==
|
||||
LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
String shortcutId = intent.getStringExtra(
|
||||
ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
|
||||
String packageName = intent.getPackage();
|
||||
ShortcutKey key = new ShortcutKey(intent.getPackage(),
|
||||
user, shortcutId);
|
||||
ShortcutInfoCompat pinnedShortcut =
|
||||
shortcutKeyToPinnedShortcuts.get(key);
|
||||
boolean shouldPin = false; // It's already pinned.
|
||||
if (pinnedShortcut == null) {
|
||||
// It shouldn't be possible for a shortcut to be on the
|
||||
// workspace without being pinned, but if one somehow is,
|
||||
// we should pin it now to get back to a good state.
|
||||
Log.w(TAG, "Shortcut was on workspace but wasn't pinned");
|
||||
// Get full details; incrementing the count will pin it.
|
||||
List<ShortcutInfoCompat> fullDetails = mDeepShortcutManager
|
||||
.queryForFullDetails(packageName,
|
||||
Collections.singletonList(shortcutId), user);
|
||||
if (fullDetails == null || fullDetails.isEmpty()) {
|
||||
itemsToRemove.add(id);
|
||||
continue;
|
||||
} else {
|
||||
pinnedShortcut = fullDetails.get(0);
|
||||
shouldPin = true;
|
||||
}
|
||||
}
|
||||
incrementPinnedShortcutCount(key, shouldPin);
|
||||
info = ShortcutInfo.fromDeepShortcutInfo(pinnedShortcut,
|
||||
context, launcherApps);
|
||||
} else { // item type == ITEM_TYPE_SHORTCUT
|
||||
info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
|
||||
|
||||
// Shortcuts are only available on the primary profile
|
||||
@@ -2098,6 +2222,15 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
|
||||
// Unpin shortcuts that don't exist on the workspace.
|
||||
for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
|
||||
MutableInt numTimesPinned = sBgPinnedShortcutCounts.get(key);
|
||||
if (numTimesPinned == null || numTimesPinned.value == 0) {
|
||||
// Shortcut is pinned but doesn't exist on the workspace; unpin it.
|
||||
mDeepShortcutManager.unpinShortcut(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all the folder items and make sure the first 3 items are high resolution.
|
||||
for (FolderInfo folder : sBgFolders) {
|
||||
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
|
||||
@@ -2629,6 +2762,27 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndBindDeepShortcuts() {
|
||||
if (DEBUG_LOADERS) {
|
||||
Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
|
||||
}
|
||||
if (!mDeepShortcutsLoaded) {
|
||||
mBgDeepShortcutMap.clear();
|
||||
for (UserHandleCompat user : mUserManager.getUserProfiles()) {
|
||||
List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager
|
||||
.queryForAllShortcuts(user);
|
||||
updateDeepShortcutMap(null, shortcuts);
|
||||
}
|
||||
synchronized (LoaderTask.this) {
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
mDeepShortcutsLoaded = true;
|
||||
}
|
||||
}
|
||||
bindDeepShortcutMapOnMainThread();
|
||||
}
|
||||
|
||||
public void dumpState() {
|
||||
synchronized (sBgLock) {
|
||||
Log.d(TAG, "mLoaderTask.mContext=" + mContext);
|
||||
@@ -2639,6 +2793,40 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all the shortcuts for the given package, and re-add the new shortcuts.
|
||||
private void updateDeepShortcutMap(String packageName, List<ShortcutInfoCompat> shortcuts) {
|
||||
// Remove all keys associated with the given package.
|
||||
if (packageName != null) {
|
||||
Iterator<ComponentKey> keysIter = mBgDeepShortcutMap.keySet().iterator();
|
||||
while (keysIter.hasNext()) {
|
||||
if (keysIter.next().componentName.getPackageName().equals(packageName)) {
|
||||
keysIter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now add the new shortcuts to the map.
|
||||
for (ShortcutInfoCompat shortcut : shortcuts) {
|
||||
ComponentKey targetComponent
|
||||
= new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
|
||||
mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void bindDeepShortcutMapOnMainThread() {
|
||||
final MultiHashMap<ComponentKey, String> shortcutMapCopy = new MultiHashMap<>();
|
||||
shortcutMapCopy.putAll(mBgDeepShortcutMap);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Callbacks callbacks = getCallback();
|
||||
if (callbacks != null) {
|
||||
callbacks.bindDeepShortcutMap(shortcutMapCopy);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the icons for packages have been updated in the icon cache.
|
||||
*/
|
||||
@@ -2664,19 +2852,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
|
||||
}
|
||||
|
||||
if (!updatedShortcuts.isEmpty()) {
|
||||
final UserHandleCompat userFinal = user;
|
||||
mHandler.post(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
Callbacks cb = getCallback();
|
||||
if (cb != null && callbacks == cb) {
|
||||
cb.bindShortcutsChanged(updatedShortcuts,
|
||||
new ArrayList<ShortcutInfo>(), userFinal);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
bindUpdatedShortcuts(updatedShortcuts, user);
|
||||
|
||||
if (!updatedApps.isEmpty()) {
|
||||
mHandler.post(new Runnable() {
|
||||
@@ -2691,7 +2867,25 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
|
||||
void enqueuePackageUpdated(PackageUpdatedTask task) {
|
||||
private void bindUpdatedShortcuts(final ArrayList<ShortcutInfo> updatedShortcuts,
|
||||
UserHandleCompat user) {
|
||||
if (!updatedShortcuts.isEmpty()) {
|
||||
final Callbacks callbacks = getCallback();
|
||||
final UserHandleCompat userFinal = user;
|
||||
mHandler.post(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
Callbacks cb = getCallback();
|
||||
if (cb != null && callbacks == cb) {
|
||||
cb.bindShortcutsChanged(updatedShortcuts,
|
||||
new ArrayList<ShortcutInfo>(), userFinal);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void enqueueItemUpdatedTask(Runnable task) {
|
||||
sWorker.post(task);
|
||||
}
|
||||
|
||||
@@ -2719,11 +2913,11 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
if (!packagesRemoved.isEmpty()) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
|
||||
packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
|
||||
}
|
||||
if (!packagesUnavailable.isEmpty()) {
|
||||
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
|
||||
enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
|
||||
packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
|
||||
}
|
||||
}
|
||||
@@ -3081,6 +3275,60 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
|
||||
private class ShortcutsChangedTask implements Runnable {
|
||||
private String mPackageName;
|
||||
private List<ShortcutInfoCompat> mShortcuts;
|
||||
private UserHandleCompat mUser;
|
||||
|
||||
public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
|
||||
UserHandleCompat user) {
|
||||
mPackageName = packageName;
|
||||
mShortcuts = shortcuts;
|
||||
mUser = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mDeepShortcutManager.onShortcutsChanged(mShortcuts);
|
||||
|
||||
Map<String, ShortcutInfoCompat> idsToShortcuts = new HashMap<>();
|
||||
for (ShortcutInfoCompat shortcut : mShortcuts) {
|
||||
idsToShortcuts.put(shortcut.getId(), shortcut);
|
||||
}
|
||||
|
||||
// Find ShortcutInfo's that have changed on the workspace.
|
||||
MultiHashMap<String, ShortcutInfo> idsToWorkspaceShortcutInfos = new MultiHashMap<>();
|
||||
for (ItemInfo itemInfo : sBgItemsIdMap) {
|
||||
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
ShortcutInfo si = (ShortcutInfo) itemInfo;
|
||||
String shortcutId = si.getDeepShortcutId();
|
||||
if (idsToShortcuts.containsKey(shortcutId)) {
|
||||
idsToWorkspaceShortcutInfos.addToList(shortcutId, si);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the workspace to reflect the changes to updated shortcuts residing on it.
|
||||
List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager.queryForFullDetails(
|
||||
mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser);
|
||||
ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
|
||||
Context context = LauncherAppState.getInstance().getContext();
|
||||
for (ShortcutInfoCompat fullDetails : shortcuts) {
|
||||
List<ShortcutInfo> shortcutInfos = idsToWorkspaceShortcutInfos
|
||||
.get(fullDetails.getId());
|
||||
for (ShortcutInfo shortcutInfo : shortcutInfos) {
|
||||
shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context, mLauncherApps);
|
||||
updatedShortcutInfos.add(shortcutInfo);
|
||||
}
|
||||
}
|
||||
bindUpdatedShortcuts(updatedShortcutInfos, mUser);
|
||||
|
||||
// Update the deep shortcut map, in case the list of ids has changed for an activity.
|
||||
updateDeepShortcutMap(mPackageName, mShortcuts);
|
||||
bindDeepShortcutMapOnMainThread();
|
||||
}
|
||||
}
|
||||
|
||||
private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -210,6 +210,11 @@ public class LauncherSettings {
|
||||
*/
|
||||
public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
|
||||
|
||||
/**
|
||||
* The gesture is an application created deep shortcut
|
||||
*/
|
||||
public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
|
||||
|
||||
/**
|
||||
* The appWidgetId of the widget
|
||||
*
|
||||
|
||||
@@ -16,20 +16,23 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
|
||||
/**
|
||||
* Represents a launchable icon on the workspaces and in folders.
|
||||
@@ -274,6 +277,46 @@ public class ShortcutInfo extends ItemInfo {
|
||||
return shortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}. Pardon the overloaded name.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public static ShortcutInfo fromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo,
|
||||
Context context, LauncherAppsCompat launcherApps) {
|
||||
ShortcutInfo si = new ShortcutInfo();
|
||||
si.user = shortcutInfo.getUserHandle();
|
||||
si.itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
si.intent = shortcutInfo.makeIntent(context);
|
||||
si.flags = 0;
|
||||
si.updateFromDeepShortcutInfo(shortcutInfo, context, launcherApps);
|
||||
return si;
|
||||
}
|
||||
|
||||
public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo,
|
||||
Context context, LauncherAppsCompat launcherApps) {
|
||||
title = shortcutInfo.getShortLabel();
|
||||
|
||||
CharSequence label = shortcutInfo.getLongLabel();
|
||||
if (TextUtils.isEmpty(label)) {
|
||||
label = shortcutInfo.getShortLabel();
|
||||
}
|
||||
this.contentDescription = UserManagerCompat.getInstance(context)
|
||||
.getBadgedLabelForUser(label, user);
|
||||
|
||||
LauncherAppState launcherAppState = LauncherAppState.getInstance();
|
||||
Drawable unbadgedIcon = launcherApps.getShortcutIconDrawable(shortcutInfo, launcherAppState
|
||||
.getInvariantDeviceProfile().fillResIconDpi);
|
||||
Bitmap icon = unbadgedIcon == null ? null
|
||||
: Utilities.createBadgedIconBitmap(unbadgedIcon, user, context);
|
||||
setIcon(icon != null ? icon : launcherAppState.getIconCache().getDefaultIcon(user));
|
||||
}
|
||||
|
||||
/** Returns the ShortcutInfo id associated with the deep shortcut. */
|
||||
public String getDeepShortcutId() {
|
||||
return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
|
||||
intent.getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
return isDisabled != 0;
|
||||
|
||||
@@ -59,7 +59,6 @@ import android.widget.TextView;
|
||||
import com.android.launcher3.Launcher.CustomContentCallbacks;
|
||||
import com.android.launcher3.Launcher.LauncherOverlay;
|
||||
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
|
||||
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
||||
@@ -2575,7 +2574,8 @@ public class Workspace extends PagedView
|
||||
boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
|
||||
boolean willBecomeShortcut =
|
||||
(info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
|
||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
|
||||
|
||||
return (aboveShortcut && willBecomeShortcut);
|
||||
}
|
||||
@@ -3509,6 +3509,7 @@ public class Workspace extends PagedView
|
||||
switch (info.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
if (info.container == NO_ID && info instanceof AppInfo) {
|
||||
// Came from all apps -- make a copy
|
||||
info = ((AppInfo) info).makeShortcut();
|
||||
|
||||
@@ -19,10 +19,13 @@ package com.android.launcher3.compat;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -45,6 +48,8 @@ public abstract class LauncherAppsCompat {
|
||||
void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
|
||||
void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
|
||||
void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
|
||||
void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
|
||||
UserHandleCompat user);
|
||||
}
|
||||
|
||||
protected LauncherAppsCompat() {
|
||||
@@ -56,7 +61,9 @@ public abstract class LauncherAppsCompat {
|
||||
public static LauncherAppsCompat getInstance(Context context) {
|
||||
synchronized (sInstanceLock) {
|
||||
if (sInstance == null) {
|
||||
if (Utilities.ATLEAST_LOLLIPOP) {
|
||||
if (Utilities.isNycOrAbove()) {
|
||||
sInstance = new LauncherAppsCompatVNMR1(context.getApplicationContext());
|
||||
} else if (Utilities.ATLEAST_LOLLIPOP) {
|
||||
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
|
||||
} else {
|
||||
sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
|
||||
@@ -79,4 +86,11 @@ public abstract class LauncherAppsCompat {
|
||||
public abstract boolean isActivityEnabledForProfile(ComponentName component,
|
||||
UserHandleCompat user);
|
||||
public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
|
||||
public abstract List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
|
||||
UserHandleCompat userHandle);
|
||||
public abstract void pinShortcuts(String packageName, List<String> pinnedIds,
|
||||
UserHandleCompat userHandle);
|
||||
public abstract void startShortcut(String packageName, String id, Rect sourceBounds,
|
||||
Bundle startActivityOptions, UserHandleCompat user);
|
||||
public abstract Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density);
|
||||
}
|
||||
|
||||
@@ -22,15 +22,18 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
@@ -130,6 +133,29 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
|
||||
UserHandleCompat userHandle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pinShortcuts(String packageName, List<String> pinnedIds,
|
||||
UserHandleCompat userHandle) {
|
||||
// Not supported, so do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startShortcut(String packageName, String id, Rect sourceBounds,
|
||||
Bundle startActivityOptions, UserHandleCompat user) {
|
||||
// Not supported, so do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void unregisterForPackageIntents() {
|
||||
mContext.unregisterReceiver(mPackageMonitor);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,14 @@ 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.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -34,7 +37,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class LauncherAppsCompatVL extends LauncherAppsCompat {
|
||||
public class LauncherAppsCompatVL extends LauncherAppsCompatV16 {
|
||||
|
||||
protected LauncherApps mLauncherApps;
|
||||
|
||||
@@ -42,7 +45,7 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
|
||||
= new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
|
||||
|
||||
LauncherAppsCompatVL(Context context) {
|
||||
super();
|
||||
super(context);
|
||||
mLauncherApps = (LauncherApps) context.getSystemService("launcherapps");
|
||||
}
|
||||
|
||||
@@ -146,6 +149,18 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
|
||||
public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
|
||||
mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
|
||||
UserHandle user) {
|
||||
List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
|
||||
for (ShortcutInfo shortcutInfo : shortcuts) {
|
||||
shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
|
||||
}
|
||||
|
||||
mCallback.onShortcutsChanged(packageName, shortcutInfoCompats,
|
||||
UserHandleCompat.fromUser(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.compat;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class LauncherAppsCompatVNMR1 extends LauncherAppsCompatVL {
|
||||
|
||||
LauncherAppsCompatVNMR1(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
|
||||
UserHandleCompat userHandle) {
|
||||
List<ShortcutInfo> shortcutInfos = mLauncherApps.getShortcuts(q, userHandle.getUser());
|
||||
if (shortcutInfos == null) {
|
||||
return null;
|
||||
}
|
||||
List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
|
||||
for (ShortcutInfo shortcutInfo : shortcutInfos) {
|
||||
shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
|
||||
}
|
||||
return shortcutInfoCompats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pinShortcuts(String packageName, List<String> pinnedIds,
|
||||
UserHandleCompat userHandle) {
|
||||
mLauncherApps.pinShortcuts(packageName, pinnedIds, userHandle.getUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startShortcut(String packageName, String id, Rect sourceBounds,
|
||||
Bundle startActivityOptions, UserHandleCompat user) {
|
||||
mLauncherApps.startShortcut(packageName, id, sourceBounds,
|
||||
startActivityOptions, user.getUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
|
||||
return mLauncherApps.getShortcutIconDrawable(shortcutInfo.getShortcutInfo(), density);
|
||||
}
|
||||
|
||||
private static class WrappedCallback extends LauncherApps.Callback {
|
||||
private OnAppsChangedCallbackCompat mCallback;
|
||||
|
||||
public WrappedCallback(OnAppsChangedCallbackCompat callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public void onPackageRemoved(String packageName, UserHandle user) {
|
||||
mCallback.onPackageRemoved(packageName, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
public void onPackageAdded(String packageName, UserHandle user) {
|
||||
mCallback.onPackageAdded(packageName, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
public void onPackageChanged(String packageName, UserHandle user) {
|
||||
mCallback.onPackageChanged(packageName, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
|
||||
mCallback.onPackagesAvailable(packageNames, UserHandleCompat.fromUser(user), replacing);
|
||||
}
|
||||
|
||||
public void onPackagesUnavailable(String[] packageNames, UserHandle user,
|
||||
boolean replacing) {
|
||||
mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
|
||||
replacing);
|
||||
}
|
||||
|
||||
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
|
||||
mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
|
||||
mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
|
||||
UserHandle user) {
|
||||
List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
|
||||
for (ShortcutInfo shortcutInfo : shortcuts) {
|
||||
shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
|
||||
}
|
||||
|
||||
mCallback.onShortcutsChanged(packageName, shortcutInfoCompats,
|
||||
UserHandleCompat.fromUser(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
}
|
||||
|
||||
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
|
||||
// leads to an consistent state if you drag out of the folder and drag back in without
|
||||
// leads to an inconsistent state if you drag out of the folder and drag back in without
|
||||
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
|
||||
mDeleteFolderOnDropCompleted = false;
|
||||
|
||||
@@ -737,7 +737,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
final ItemInfo item = d.dragInfo;
|
||||
final int itemType = item.itemType;
|
||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
|
||||
!isFull());
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
private boolean willAcceptItem(ItemInfo item) {
|
||||
final int itemType = item.itemType;
|
||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
|
||||
!mFolder.isFull() && item != mInfo && !mInfo.opened);
|
||||
}
|
||||
|
||||
|
||||
@@ -641,10 +641,11 @@ public class GridSizeMigrationTask {
|
||||
// calculate weight
|
||||
switch (entry.itemType) {
|
||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case Favorites.ITEM_TYPE_APPLICATION: {
|
||||
verifyIntent(c.getString(indexIntent));
|
||||
entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
? WT_SHORTCUT : WT_APPLICATION;
|
||||
entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
|
||||
WT_APPLICATION : WT_SHORTCUT;
|
||||
break;
|
||||
}
|
||||
case Favorites.ITEM_TYPE_FOLDER: {
|
||||
@@ -715,10 +716,11 @@ public class GridSizeMigrationTask {
|
||||
// calculate weight
|
||||
switch (entry.itemType) {
|
||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case Favorites.ITEM_TYPE_APPLICATION: {
|
||||
verifyIntent(c.getString(indexIntent));
|
||||
entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
? WT_SHORTCUT : WT_APPLICATION;
|
||||
entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
|
||||
WT_APPLICATION : WT_SHORTCUT;
|
||||
break;
|
||||
}
|
||||
case Favorites.ITEM_TYPE_APPWIDGET: {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.shortcuts;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class DeepShortcutManager {
|
||||
private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_GET_DYNAMIC
|
||||
| ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_MANIFEST;
|
||||
|
||||
private final LauncherAppsCompat mLauncherApps;
|
||||
|
||||
public DeepShortcutManager(Context context, ShortcutCache shortcutCache) {
|
||||
mLauncherApps = LauncherAppsCompat.getInstance(context);
|
||||
}
|
||||
|
||||
public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
|
||||
// mShortcutCache.removeShortcuts(shortcuts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries for the shortcuts with the package name and provided ids.
|
||||
*
|
||||
* This method is intended to get the full details for shortcuts when they are added or updated,
|
||||
* because we only get "key" fields in onShortcutsChanged().
|
||||
*/
|
||||
public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
|
||||
List<String> shortcutIds, UserHandleCompat user) {
|
||||
return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the shortcuts associated with the given package and user.
|
||||
*/
|
||||
public List<ShortcutInfoCompat> queryForAllAppShortcuts(ComponentName activity,
|
||||
List<String> ids, UserHandleCompat user) {
|
||||
return query(FLAG_GET_ALL, activity.getPackageName(), activity, ids, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given shortcut from the current list of pinned shortcuts.
|
||||
* (Runs on background thread)
|
||||
*/
|
||||
public void unpinShortcut(final ShortcutKey key) {
|
||||
String packageName = key.componentName.getPackageName();
|
||||
String id = key.id;
|
||||
UserHandleCompat user = key.user;
|
||||
List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
|
||||
pinnedIds.remove(id);
|
||||
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given shortcut to the current list of pinned shortcuts.
|
||||
* (Runs on background thread)
|
||||
*/
|
||||
public void pinShortcut(final ShortcutKey key) {
|
||||
String packageName = key.componentName.getPackageName();
|
||||
String id = key.id;
|
||||
UserHandleCompat user = key.user;
|
||||
List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
|
||||
pinnedIds.add(id);
|
||||
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id's of pinned shortcuts associated with the given package and user.
|
||||
*
|
||||
* If packageName is null, returns all pinned shortcuts regardless of package.
|
||||
*/
|
||||
public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
|
||||
UserHandleCompat user) {
|
||||
return query(ShortcutQuery.FLAG_GET_PINNED, packageName, null, null, user);
|
||||
}
|
||||
|
||||
public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandleCompat user) {
|
||||
return query(FLAG_GET_ALL, null, null, null, user);
|
||||
}
|
||||
|
||||
private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
|
||||
List<String> shortcutIds = new ArrayList<>(shortcuts.size());
|
||||
for (ShortcutInfoCompat shortcut : shortcuts) {
|
||||
shortcutIds.add(shortcut.getId());
|
||||
}
|
||||
return shortcutIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the system server for all the shortcuts matching the given parameters.
|
||||
* If packageName == null, we query for all shortcuts with the passed flags, regardless of app.
|
||||
*
|
||||
* TODO: Use the cache to optimize this so we don't make an RPC every time.
|
||||
*/
|
||||
private List<ShortcutInfoCompat> query(int flags, String packageName,
|
||||
ComponentName activity, List<String> shortcutIds, UserHandleCompat user) {
|
||||
ShortcutQuery q = new ShortcutQuery();
|
||||
q.setQueryFlags(flags);
|
||||
if (packageName != null) {
|
||||
q.setPackage(packageName);
|
||||
q.setActivity(activity);
|
||||
q.setShortcutIds(shortcutIds);
|
||||
}
|
||||
return mLauncherApps.getShortcuts(q, user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.shortcuts;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.util.LruCache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Loads {@link ShortcutInfoCompat}s on demand (e.g. when launcher
|
||||
* loads for pinned shortcuts and on long-press for dynamic shortcuts), and caches them
|
||||
* for handful of apps in an LruCache while launcher lives.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class ShortcutCache {
|
||||
private static final String TAG = "ShortcutCache";
|
||||
private static final boolean LOGD = false;
|
||||
|
||||
private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
|
||||
|
||||
private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
|
||||
// We always keep pinned shortcuts in the cache.
|
||||
private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
|
||||
|
||||
public ShortcutCache() {
|
||||
mCachedShortcuts = new LruCache<>(CACHE_SIZE);
|
||||
mPinnedShortcuts = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes shortcuts from the cache when shortcuts change for a given package.
|
||||
*
|
||||
* Returns a map of ids to their evicted shortcuts.
|
||||
*
|
||||
* @see android.content.pm.LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle).
|
||||
*/
|
||||
public void removeShortcuts(List<ShortcutInfoCompat> shortcuts) {
|
||||
for (ShortcutInfoCompat shortcut : shortcuts) {
|
||||
ShortcutKey key = ShortcutKey.fromInfo(shortcut);
|
||||
mCachedShortcuts.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public ShortcutInfoCompat get(ShortcutKey key) {
|
||||
if (mPinnedShortcuts.containsKey(key)) {
|
||||
return mPinnedShortcuts.get(key);
|
||||
}
|
||||
return mCachedShortcuts.get(key);
|
||||
}
|
||||
|
||||
public void put(ShortcutKey key, ShortcutInfoCompat shortcut) {
|
||||
if (shortcut.isPinned()) {
|
||||
mPinnedShortcuts.put(key, shortcut);
|
||||
} else {
|
||||
mCachedShortcuts.put(key, shortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.shortcuts;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
|
||||
/**
|
||||
* Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
|
||||
*
|
||||
* Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class ShortcutInfoCompat {
|
||||
private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
|
||||
public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
|
||||
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
|
||||
public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
|
||||
mShortcutInfo = shortcutInfo;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public Intent makeIntent(Context context) {
|
||||
long serialNumber = UserManagerCompat.getInstance(context)
|
||||
.getSerialNumberForUser(getUserHandle());
|
||||
return new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(INTENT_CATEGORY)
|
||||
.setComponent(getActivity())
|
||||
.setPackage(getPackage())
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
|
||||
.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber)
|
||||
.putExtra(EXTRA_SHORTCUT_ID, getId());
|
||||
}
|
||||
|
||||
public ShortcutInfo getShortcutInfo() {
|
||||
return mShortcutInfo;
|
||||
}
|
||||
|
||||
public String getPackage() {
|
||||
return mShortcutInfo.getPackage();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mShortcutInfo.getId();
|
||||
}
|
||||
|
||||
public CharSequence getShortLabel() {
|
||||
return mShortcutInfo.getShortLabel();
|
||||
}
|
||||
|
||||
public CharSequence getLongLabel() {
|
||||
return mShortcutInfo.getLongLabel();
|
||||
}
|
||||
|
||||
public long getLastChangedTimestamp() {
|
||||
return mShortcutInfo.getLastChangedTimestamp();
|
||||
}
|
||||
|
||||
public ComponentName getActivity() {
|
||||
return mShortcutInfo.getActivity();
|
||||
}
|
||||
|
||||
public UserHandleCompat getUserHandle() {
|
||||
return UserHandleCompat.fromUser(mShortcutInfo.getUserHandle());
|
||||
}
|
||||
|
||||
public boolean hasKeyFieldsOnly() {
|
||||
return mShortcutInfo.hasKeyFieldsOnly();
|
||||
}
|
||||
|
||||
public boolean isPinned() {
|
||||
return mShortcutInfo.isPinned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mShortcutInfo.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.android.launcher3.shortcuts;
|
||||
|
||||
import android.content.ComponentName;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
|
||||
/**
|
||||
* A key that uniquely identifies a shortcut using its package, id, and user handle.
|
||||
*/
|
||||
public class ShortcutKey extends ComponentKey {
|
||||
final String id;
|
||||
|
||||
public ShortcutKey(String packageName, UserHandleCompat user, String id) {
|
||||
// Use the id as the class name.
|
||||
super(new ComponentName(packageName, id), user);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
|
||||
return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
|
||||
shortcutInfo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return flattenToString(LauncherAppState.getInstance().getContext());
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutInfo;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
|
||||
@@ -180,6 +181,12 @@ public class ManagedProfileHeuristic {
|
||||
saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
|
||||
UserHandleCompat user) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.android.launcher3.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A utility map from keys to an ArrayList of values.
|
||||
*/
|
||||
public class MultiHashMap<K, V> extends HashMap<K, ArrayList<V>> {
|
||||
public void addToList(K key, V value) {
|
||||
ArrayList<V> list = get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
list.add(value);
|
||||
put(key, list);
|
||||
} else {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user