Implement ability to uninstall shortcuts from workspace

Change-Id: I14ca94ea719825c4de19ab85d67537155098d50c
This commit is contained in:
Michael Jurka
2013-07-08 18:03:46 -07:00
parent 766f5f7a53
commit 1e2f465f46
10 changed files with 239 additions and 67 deletions
+1 -1
View File
@@ -166,7 +166,7 @@ class AllAppsList {
/**
* Query the package manager for MAIN/LAUNCHER activities in the supplied package.
*/
private static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
final PackageManager packageManager = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+21 -9
View File
@@ -20,6 +20,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.util.Log;
@@ -72,15 +73,9 @@ class ApplicationInfo extends ItemInfo {
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
try {
int appFlags = pm.getApplicationInfo(packageName, 0).flags;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
flags |= DOWNLOADED_FLAG;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
flags |= UPDATED_SYSTEM_APP_FLAG;
}
}
firstInstallTime = pm.getPackageInfo(packageName, 0).firstInstallTime;
PackageInfo pi = pm.getPackageInfo(packageName, 0);
flags = initFlags(pi);
firstInstallTime = initFirstInstallTime(pi);
} catch (NameNotFoundException e) {
Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);
}
@@ -88,6 +83,23 @@ class ApplicationInfo extends ItemInfo {
iconCache.getTitleAndIcon(this, info, labelCache);
}
public static int initFlags(PackageInfo pi) {
int appFlags = pi.applicationInfo.flags;
int flags = 0;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
flags |= DOWNLOADED_FLAG;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
flags |= UPDATED_SYSTEM_APP_FLAG;
}
}
return flags;
}
public static long initFirstInstallTime(PackageInfo pi) {
return pi.firstInstallTime;
}
public ApplicationInfo(ApplicationInfo info) {
super(info);
componentName = info.componentName;
@@ -19,7 +19,11 @@ package com.android.launcher3;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -27,6 +31,7 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
@@ -34,6 +39,8 @@ import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import java.util.List;
public class DeleteDropTarget extends ButtonDropTarget {
private static int DELETE_ANIMATION_DURATION = 285;
private static int FLING_DELETE_ANIMATION_DURATION = 350;
@@ -136,6 +143,12 @@ public class DeleteDropTarget extends ButtonDropTarget {
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
return true;
}
if (AppsCustomizePagedView.DISABLE_ALL_APPS &&
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
item instanceof ShortcutInfo) {
ShortcutInfo shortcutInfo = (ShortcutInfo) info;
return (shortcutInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0;
}
}
return false;
}
@@ -198,20 +211,22 @@ public class DeleteDropTarget extends ButtonDropTarget {
}
private void animateToTrashAndCompleteDrop(final DragObject d) {
DragLayer dragLayer = mLauncher.getDragLayer();
Rect from = new Rect();
final DragLayer dragLayer = mLauncher.getDragLayer();
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight());
float scale = (float) to.width() / from.width();
final float scale = (float) to.width() / from.width();
mSearchDropTargetBar.deferOnDragEnd();
deferCompleteDropIfUninstalling(d);
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
completeDrop(d);
mSearchDropTargetBar.onDragEnd();
mLauncher.exitSpringLoadedDragMode();
completeDrop(d);
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
@@ -220,12 +235,40 @@ public class DeleteDropTarget extends ButtonDropTarget {
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
private void completeDrop(DragObject d) {
ItemInfo item = (ItemInfo) d.dragInfo;
private void deferCompleteDropIfUninstalling(DragObject d) {
mWaitingForUninstall = false;
if (isUninstall(d)) {
if (d.dragSource instanceof Folder) {
((Folder) d.dragSource).deferCompleteDropAfterUninstallActivity();
} else if (d.dragSource instanceof Workspace) {
((Workspace) d.dragSource).deferCompleteDropAfterUninstallActivity();
}
mWaitingForUninstall = true;
}
}
private boolean isUninstall(DragObject d) {
return AppsCustomizePagedView.DISABLE_ALL_APPS && isWorkspaceOrFolderApplication(d);
}
private boolean mWaitingForUninstall = false;
private void completeDrop(final DragObject d) {
ItemInfo item = (ItemInfo) d.dragInfo;
boolean wasWaitingForUninstall = mWaitingForUninstall;
mWaitingForUninstall = false;
if (isAllAppsApplication(d.dragSource, item)) {
// Uninstall the application if it is being dragged from AppsCustomize
mLauncher.startApplicationUninstallActivity((ApplicationInfo) item);
ApplicationInfo appInfo = (ApplicationInfo) item;
mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags);
} else if (AppsCustomizePagedView.DISABLE_ALL_APPS && isWorkspaceOrFolderApplication(d)) {
ShortcutInfo shortcut = (ShortcutInfo) item;
if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
ComponentName componentName = shortcut.intent.getComponent();
int flags = ApplicationInfo.initFlags(
ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName()));
mWaitingForUninstall =
mLauncher.startApplicationUninstallActivity(componentName, flags);
}
} else if (isWorkspaceOrFolderApplication(d)) {
LauncherModel.deleteItemFromDatabase(mLauncher, item);
} else if (isWorkspaceFolder(d)) {
@@ -250,6 +293,37 @@ public class DeleteDropTarget extends ButtonDropTarget {
}.start();
}
}
if (wasWaitingForUninstall && !mWaitingForUninstall) {
if (d.dragSource instanceof Folder) {
((Folder) d.dragSource).onUninstallActivityReturned(false);
} else if (d.dragSource instanceof Workspace) {
((Workspace) d.dragSource).onUninstallActivityReturned(false);
}
}
if (mWaitingForUninstall) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
mWaitingForUninstall = false;
ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo;
if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
String packageName = shortcut.intent.getComponent().getPackageName();
List<ResolveInfo> activities =
AllAppsList.findActivitiesForPackage(getContext(), packageName);
boolean uninstallSuccessful = activities.size() == 0;
mLauncher.removeOnResumeCallback(this);
if (d.dragSource instanceof Folder) {
((Folder) d.dragSource).
onUninstallActivityReturned(uninstallSuccessful);
} else if (d.dragSource instanceof Workspace) {
((Workspace) d.dragSource).
onUninstallActivityReturned(uninstallSuccessful);
}
}
}
};
mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
}
}
public void onDrop(DragObject d) {
@@ -422,6 +496,8 @@ public class DeleteDropTarget extends ButtonDropTarget {
updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
duration, config);
}
deferCompleteDropIfUninstalling(d);
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
@@ -379,9 +379,11 @@ public class DragController {
void onDeferredEndDrag(DragView dragView) {
dragView.remove();
// If we skipped calling onDragEnd() before, do it now
for (DragListener listener : mListeners) {
listener.onDragEnd();
if (mDragObject.deferDragViewCleanupPostAnimation) {
// If we skipped calling onDragEnd() before, do it now
for (DragListener listener : mListeners) {
listener.onDragEnd();
}
}
}
+33 -4
View File
@@ -126,6 +126,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private AutoScroller mAutoScroller;
private Runnable mDeferredAction;
private boolean mDeferDropAfterUninstall;
private boolean mUninstallSuccessful;
/**
* Used to inflate the Workspace from XML.
*
@@ -743,9 +747,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mDragMode = DRAG_MODE_NONE;
}
public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
boolean success) {
if (success) {
public void onDropCompleted(final View target, final DragObject d,
final boolean isFlingToDelete, final boolean success) {
if (mDeferDropAfterUninstall) {
mDeferredAction = new Runnable() {
public void run() {
onDropCompleted(target, d, isFlingToDelete, success);
mDeferredAction = null;
}
};
return;
}
boolean beingCalledAfterUninstall = mDeferredAction != null;
boolean successfulDrop =
success && (!beingCalledAfterUninstall || mUninstallSuccessful);
if (successfulDrop) {
if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon) {
replaceFolderWithFinalItem();
}
@@ -758,7 +775,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (target != this) {
if (mOnExitAlarm.alarmPending()) {
mOnExitAlarm.cancelAlarm();
if (!success) {
if (successfulDrop) {
mSuppressFolderDeletion = true;
}
completeDragExit();
@@ -776,6 +793,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
updateItemLocationsInDatabaseBatch();
}
public void deferCompleteDropAfterUninstallActivity() {
mDeferDropAfterUninstall = true;
}
public void onUninstallActivityReturned(boolean success) {
mDeferDropAfterUninstall = false;
mUninstallSuccessful = success;
if (mDeferredAction != null) {
mDeferredAction.run();
}
}
@Override
public boolean supportsFlingToDelete() {
return true;
-15
View File
@@ -118,21 +118,6 @@ class ItemInfo {
LauncherModel.checkItemInfo(this);
}
/** Returns the package name that the intent will resolve to, or an empty string if
* none exists. */
static String getPackageName(Intent intent) {
if (intent != null) {
String packageName = intent.getPackage();
if (packageName == null && intent.getComponent() != null) {
packageName = intent.getComponent().getPackageName();
}
if (packageName != null) {
return packageName;
}
}
return "";
}
/**
* Write the fields of this item to the DB
*
+24 -12
View File
@@ -255,6 +255,7 @@ public class Launcher extends Activity
private boolean mWaitingForResult;
private boolean mOnResumeNeedsLoad;
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
// Keep track of whether the user has left launcher
@@ -770,7 +771,7 @@ public class Launcher extends Activity
mRestoring = false;
mOnResumeNeedsLoad = false;
}
if (mOnResumeCallbacks.size() > 0) {
if (mBindOnResumeCallbacks.size() > 0) {
// We might have postponed some bind calls until onResume (see waitUntilResume) --
// execute them here
long startTimeCallbacks = 0;
@@ -781,13 +782,13 @@ public class Launcher extends Activity
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setBulkBind(true);
}
for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
mOnResumeCallbacks.get(i).run();
for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
mBindOnResumeCallbacks.get(i).run();
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setBulkBind(false);
}
mOnResumeCallbacks.clear();
mBindOnResumeCallbacks.clear();
if (DEBUG_RESUME_TIME) {
Log.d(TAG, "Time spent processing callbacks in onResume: " +
(System.currentTimeMillis() - startTimeCallbacks));
@@ -1101,7 +1102,7 @@ public class Launcher extends Activity
final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
if (info != null) {
info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
info.container = ItemInfo.NO_ID;
mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1],
@@ -2160,20 +2161,23 @@ public class Launcher extends Activity
startActivitySafely(null, intent, "startApplicationDetailsActivity");
}
void startApplicationUninstallActivity(ApplicationInfo appInfo) {
if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
// returns true if the activity was started
boolean startApplicationUninstallActivity(ComponentName componentName, int flags) {
if ((flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
int messageId = R.string.uninstall_system_app_text;
Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
return false;
} else {
String packageName = appInfo.componentName.getPackageName();
String className = appInfo.componentName.getClassName();
String packageName = componentName.getPackageName();
String className = componentName.getClassName();
Intent intent = new Intent(
Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
return true;
}
}
@@ -3370,10 +3374,10 @@ public class Launcher extends Activity
if (mPaused) {
Log.i(TAG, "Deferring update until onResume");
if (deletePreviousRunnables) {
while (mOnResumeCallbacks.remove(run)) {
while (mBindOnResumeCallbacks.remove(run)) {
}
}
mOnResumeCallbacks.add(run);
mBindOnResumeCallbacks.add(run);
return true;
} else {
return false;
@@ -3384,6 +3388,14 @@ public class Launcher extends Activity
return waitUntilResume(run, false);
}
public void addOnResumeCallback(Runnable run) {
mBindOnResumeCallbacks.add(run);
}
public void removeOnResumeCallback(Runnable run) {
mBindOnResumeCallbacks.remove(run);
}
/**
* If the activity is currently paused, signal that we need to re-run the loader
* in onResume.
@@ -3428,7 +3440,7 @@ public class Launcher extends Activity
// If we're starting binding all over again, clear any bind calls we'd postponed in
// the past (see waitUntilResume) -- we don't need them since we're starting binding
// from scratch again
mOnResumeCallbacks.clear();
mBindOnResumeCallbacks.clear();
final Workspace workspace = mWorkspace;
mNewShortcutAnimateScreenId = -1;
@@ -2494,6 +2494,7 @@ public class LauncherModel extends BroadcastReceiver {
// db and will not appear in the workspace.
return null;
}
info.initFlagsAndFirstInstallTime(pi);
} catch (NameNotFoundException e) {
Log.d(TAG, "getPackInfo failed for package " + componentName.getPackageName());
}
+31 -4
View File
@@ -16,14 +16,18 @@
package com.android.launcher3;
import java.util.ArrayList;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.util.Log;
import java.util.ArrayList;
/**
* Represents a launchable icon on the workspaces and in folders.
*/
@@ -57,11 +61,14 @@ class ShortcutInfo extends ItemInfo {
*/
private Bitmap mIcon;
long firstInstallTime;
int flags = 0;
ShortcutInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
public ShortcutInfo(ShortcutInfo info) {
public ShortcutInfo(Context context, ShortcutInfo info) {
super(info);
title = info.title.toString();
intent = new Intent(info.intent);
@@ -72,6 +79,7 @@ class ShortcutInfo extends ItemInfo {
}
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
customIcon = info.customIcon;
initFlagsAndFirstInstallTime(getPackageInfo(context, intent.getComponent().getPackageName()));
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
@@ -80,6 +88,24 @@ class ShortcutInfo extends ItemInfo {
title = info.title.toString();
intent = new Intent(info.intent);
customIcon = false;
flags = info.flags;
firstInstallTime = info.firstInstallTime;
}
public static PackageInfo getPackageInfo(Context context, String packageName) {
PackageInfo pi = null;
try {
PackageManager pm = context.getPackageManager();
pi = pm.getPackageInfo(packageName, 0);
} catch (NameNotFoundException e) {
Log.d("ShortcutInfo", "PackageManager.getPackageInfo failed for " + packageName);
}
return pi;
}
void initFlagsAndFirstInstallTime(PackageInfo pi) {
flags = ApplicationInfo.initFlags(pi);
firstInstallTime = ApplicationInfo.initFirstInstallTime(pi);
}
public void setIcon(Bitmap b) {
@@ -105,12 +131,13 @@ class ShortcutInfo extends ItemInfo {
* @param className the class name of the component representing the intent
* @param launchFlags the launch flags
*/
final void setActivity(ComponentName className, int launchFlags) {
final void setActivity(Context context, ComponentName className, int launchFlags) {
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(className);
intent.setFlags(launchFlags);
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
initFlagsAndFirstInstallTime(getPackageInfo(context, intent.getComponent().getPackageName()));
}
@Override
+39 -11
View File
@@ -250,6 +250,10 @@ public class Workspace extends SmoothPagedView
private int mLastChildCount = -1;
private float mTransitionProgress;
private Runnable mDeferredAction;
private boolean mDeferDropAfterUninstall;
private boolean mUninstallSuccessful;
private final Runnable mBindPages = new Runnable() {
@Override
public void run() {
@@ -2305,7 +2309,7 @@ public class Workspace extends SmoothPagedView
final View cell = mDragInfo.cell;
Runnable resizeRunnable = null;
if (dropTargetLayout != null) {
if (dropTargetLayout != null && !d.cancelled) {
// Move internally
boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
@@ -3411,15 +3415,25 @@ public class Workspace extends SmoothPagedView
/**
* Called at the end of a drag which originated on the workspace.
*/
public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
boolean success) {
if (success) {
if (target != this) {
if (mDragInfo != null) {
getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
if (mDragInfo.cell instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
public void onDropCompleted(final View target, final DragObject d,
final boolean isFlingToDelete, final boolean success) {
if (mDeferDropAfterUninstall) {
mDeferredAction = new Runnable() {
public void run() {
onDropCompleted(target, d, isFlingToDelete, success);
mDeferredAction = null;
}
};
return;
}
boolean beingCalledAfterUninstall = mDeferredAction != null;
if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
if (target != this && mDragInfo != null) {
getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
if (mDragInfo.cell instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
}
// If we move the item to anything not on the Workspace, check if any empty
// screens need to be removed. If we dropped back on the workspace, this will
@@ -3435,13 +3449,27 @@ public class Workspace extends SmoothPagedView
}
cellLayout.onDropChild(mDragInfo.cell);
}
if (d.cancelled && mDragInfo.cell != null) {
mDragInfo.cell.setVisibility(VISIBLE);
if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
&& mDragInfo.cell != null) {
mDragInfo.cell.setVisibility(VISIBLE);
}
mDragOutline = null;
mDragInfo = null;
}
public void deferCompleteDropAfterUninstallActivity() {
mDeferDropAfterUninstall = true;
}
/// maybe move this into a smaller part
public void onUninstallActivityReturned(boolean success) {
mDeferDropAfterUninstall = false;
mUninstallSuccessful = success;
if (mDeferredAction != null) {
mDeferredAction.run();
}
}
void updateItemLocationsInDatabase(CellLayout cl) {
int count = cl.getShortcutsAndWidgets().getChildCount();