From 76c72386989cd2adc30086fb36aa1fdd7304014b Mon Sep 17 00:00:00 2001 From: nebkat Date: Fri, 24 Feb 2012 16:03:42 +0000 Subject: DeleteDropTarget: Long-Hold To Uninstall Change-Id: I2f60040c4cc645a54e9a8e696f330a8e70ff833c --- .../cyanogenmod/trebuchet/DeleteDropTarget.java | 187 +++++++++++++-------- src/com/cyanogenmod/trebuchet/Launcher.java | 21 +++ 2 files changed, 138 insertions(+), 70 deletions(-) diff --git a/src/com/cyanogenmod/trebuchet/DeleteDropTarget.java b/src/com/cyanogenmod/trebuchet/DeleteDropTarget.java index 131882895..7c23677cb 100644 --- a/src/com/cyanogenmod/trebuchet/DeleteDropTarget.java +++ b/src/com/cyanogenmod/trebuchet/DeleteDropTarget.java @@ -17,13 +17,16 @@ package com.cyanogenmod.trebuchet; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -31,12 +34,20 @@ import android.view.animation.DecelerateInterpolator; public class DeleteDropTarget extends ButtonDropTarget { + private static final int MODE_DELETE = 0; + private static final int MODE_UNINSTALL = 1; + private int mMode = MODE_DELETE; + private static int DELETE_ANIMATION_DURATION = 250; private ColorStateList mOriginalTextColor; private int mHoverColor = 0xFFFF0000; - private TransitionDrawable mUninstallDrawable; - private TransitionDrawable mRemoveDrawable; - private TransitionDrawable mCurrentDrawable; + private Drawable mUninstallActiveDrawable; + private Drawable mRemoveActiveDrawable; + private Drawable mRemoveNormalDrawable; + private Drawable mCurrentDrawable; + private boolean mUninstall; + + private final Handler mHandler = new Handler(); public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -46,6 +57,12 @@ public class DeleteDropTarget extends ButtonDropTarget { super(context, attrs, defStyle); } + private final Runnable mShowUninstaller = new Runnable() { + public void run() { + switchToUninstallTarget(); + } + }; + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -58,16 +75,9 @@ public class DeleteDropTarget extends ButtonDropTarget { mHoverColor = r.getColor(R.color.delete_target_hover_tint); mHoverPaint.setColorFilter(new PorterDuffColorFilter( mHoverColor, PorterDuff.Mode.SRC_ATOP)); - mUninstallDrawable = (TransitionDrawable) - r.getDrawable(R.drawable.uninstall_target_selector); - mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector); - - mRemoveDrawable.setCrossFadeEnabled(true); - mUninstallDrawable.setCrossFadeEnabled(true); - - // The current drawable is set to either the remove drawable or the uninstall drawable - // and is initially set to the remove drawable, as set in the layout xml. - mCurrentDrawable = (TransitionDrawable) getCompoundDrawables()[0]; + mUninstallActiveDrawable = r.getDrawable(R.drawable.ic_launcher_trashcan_active_holo); + mRemoveActiveDrawable = r.getDrawable(R.drawable.ic_launcher_clear_active_holo); + mRemoveNormalDrawable = r.getDrawable(R.drawable.ic_launcher_clear_normal_holo); // Remove the text in the Phone UI in landscape int orientation = getResources().getConfiguration().orientation; @@ -84,17 +94,17 @@ public class DeleteDropTarget extends ButtonDropTarget { private boolean isAllAppsWidget(DragSource source, Object info) { return (source instanceof AppsCustomizeView) && (info instanceof PendingAddWidgetInfo); } - private boolean isDragSourceWorkspaceOrFolder(DragObject d) { - return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder); + private boolean isDragSourceWorkspaceOrFolder(DragSource source) { + return (source instanceof Workspace) || (source instanceof Folder); } - private boolean isWorkspaceOrFolderApplication(DragObject d) { - return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof ShortcutInfo); + private boolean isWorkspaceOrFolderApplication(DragSource source, Object info) { + return isDragSourceWorkspaceOrFolder(source) && (info instanceof ShortcutInfo); } - private boolean isWorkspaceOrFolderWidget(DragObject d) { - return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof LauncherAppWidgetInfo); + private boolean isWorkspaceWidget(DragSource source, Object info) { + return isDragSourceWorkspaceOrFolder(source) && (info instanceof LauncherAppWidgetInfo); } - private boolean isWorkspaceFolder(DragObject d) { - return (d.dragSource instanceof Workspace) && (d.dragInfo instanceof FolderInfo); + private boolean isWorkspaceFolder(DragSource source, Object info) { + return (source instanceof Workspace) && (info instanceof FolderInfo); } @Override @@ -105,60 +115,87 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override public void onDragStart(DragSource source, Object info, int dragAction) { - boolean isVisible = true; boolean isUninstall = false; - // If we are dragging a widget from AppsCustomize, hide the delete target - if (isAllAppsWidget(source, info)) { - isVisible = false; - } - - // If we are dragging an application from AppsCustomize, only show the control if we can - // delete the app (it was downloaded), and rename the string to "uninstall" in such a case + // If we are dragging an application from AppsCustomize, only show the uninstall control if we + // can delete the app (it was downloaded) if (isAllAppsApplication(source, info)) { ApplicationInfo appInfo = (ApplicationInfo) info; if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0) { isUninstall = true; - } else { - isVisible = false; + } + } else if (isWorkspaceOrFolderApplication(source, info)) { + ShortcutInfo shortcutInfo = (ShortcutInfo) info; + PackageManager pm = getContext().getPackageManager(); + ResolveInfo resolveInfo = pm.resolveActivity(shortcutInfo.intent, 0); + if (resolveInfo != null && (resolveInfo.activityInfo.applicationInfo.flags & + android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) { + isUninstall = true; } } - if (isUninstall) { - setCompoundDrawablesWithIntrinsicBounds(mUninstallDrawable, null, null, null); - } else { - setCompoundDrawablesWithIntrinsicBounds(mRemoveDrawable, null, null, null); - } - mCurrentDrawable = (TransitionDrawable) getCompoundDrawables()[0]; + setCompoundDrawablesWithIntrinsicBounds(mRemoveNormalDrawable, null, null, null); + mCurrentDrawable = getCompoundDrawables()[0]; - mActive = isVisible; - mCurrentDrawable.resetTransition(); + mUninstall = isUninstall; + mActive = true; + mMode = MODE_DELETE; setTextColor(mOriginalTextColor); - ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE); + ((ViewGroup) getParent()).setVisibility(View.VISIBLE); if (getText().length() > 0) { - setText(isUninstall ? R.string.delete_target_uninstall_label - : R.string.delete_target_label); + setText(R.string.delete_target_label); } } + private void switchToUninstallTarget() { + if (!mUninstall) { + return; + } + + mMode = MODE_UNINSTALL; + + if (getText().length() > 0) { + setText(R.string.delete_target_uninstall_label); + } + + setCompoundDrawablesWithIntrinsicBounds(mUninstallActiveDrawable, null, null, null); + mCurrentDrawable = getCompoundDrawables()[0]; + } + @Override public void onDragEnd() { super.onDragEnd(); + mActive = false; } public void onDragEnter(DragObject d) { super.onDragEnter(d); - mCurrentDrawable.startTransition(mTransitionDuration); + if (mUninstall) { + mHandler.removeCallbacks(mShowUninstaller); + mHandler.postDelayed(mShowUninstaller, 1000); + } + + setCompoundDrawablesWithIntrinsicBounds(mRemoveActiveDrawable, null, null, null); + mCurrentDrawable = getCompoundDrawables()[0]; setTextColor(mHoverColor); } public void onDragExit(DragObject d) { super.onDragExit(d); + mHandler.removeCallbacks(mShowUninstaller); + if (!d.dragComplete) { - mCurrentDrawable.resetTransition(); + mMode = MODE_DELETE; + + if (getText().length() > 0) { + setText(R.string.delete_target_label); + } + + setCompoundDrawablesWithIntrinsicBounds(mRemoveNormalDrawable, null, null, null); + mCurrentDrawable = getCompoundDrawables()[0]; setTextColor(mOriginalTextColor); } } @@ -197,32 +234,42 @@ public class DeleteDropTarget extends ButtonDropTarget { private void completeDrop(DragObject d) { ItemInfo item = (ItemInfo) d.dragInfo; - if (isAllAppsApplication(d.dragSource, item)) { - // Uninstall the application if it is being dragged from AppsCustomize - mLauncher.startApplicationUninstallActivity((ApplicationInfo) item); - } else if (isWorkspaceOrFolderApplication(d)) { - LauncherModel.deleteItemFromDatabase(mLauncher, item); - } else if (isWorkspaceFolder(d)) { - // Remove the folder from the workspace and delete the contents from launcher model - FolderInfo folderInfo = (FolderInfo) item; - mLauncher.removeFolder(folderInfo); - LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); - } else if (isWorkspaceOrFolderWidget(d)) { - // Remove the widget from the workspace - mLauncher.removeAppWidget((LauncherAppWidgetInfo) item); - LauncherModel.deleteItemFromDatabase(mLauncher, item); - - final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; - final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); - if (appWidgetHost != null) { - // Deleting an app widget ID is a void call but writes to disk before returning - // to the caller... - new Thread("deleteAppWidgetId") { - public void run() { - appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId); + switch (mMode) { + case MODE_DELETE: + if (isWorkspaceOrFolderApplication(d.dragSource, item)) { + LauncherModel.deleteItemFromDatabase(mLauncher, item); + } else if (isWorkspaceFolder(d.dragSource, d.dragInfo)) { + // Remove the folder from the workspace and delete the contents from launcher model + FolderInfo folderInfo = (FolderInfo) item; + mLauncher.removeFolder(folderInfo); + LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); + } else if (isWorkspaceWidget(d.dragSource, item)) { + // Remove the widget from the workspace + mLauncher.removeAppWidget((LauncherAppWidgetInfo) item); + LauncherModel.deleteItemFromDatabase(mLauncher, item); + + final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; + final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); + if (appWidgetHost != null) { + // Deleting an app widget ID is a void call but writes to disk before returning + // to the caller... + new Thread("deleteAppWidgetId") { + public void run() { + appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId); + } + }.start(); } - }.start(); - } + } + break; + case MODE_UNINSTALL: + if (isAllAppsApplication(d.dragSource, item)) { + // Uninstall the application + mLauncher.startApplicationUninstallActivity((ApplicationInfo) item); + } else if (isWorkspaceOrFolderApplication(d.dragSource, item)) { + // Uninstall the shortcut + mLauncher.startShortcutUninstallActivity((ShortcutInfo) item); + } + break; } } diff --git a/src/com/cyanogenmod/trebuchet/Launcher.java b/src/com/cyanogenmod/trebuchet/Launcher.java index 1d4757e33..cf1ccd88d 100644 --- a/src/com/cyanogenmod/trebuchet/Launcher.java +++ b/src/com/cyanogenmod/trebuchet/Launcher.java @@ -47,6 +47,7 @@ import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -1879,6 +1880,26 @@ public final class Launcher extends Activity } } + void startShortcutUninstallActivity(ShortcutInfo shortcutInfo) { + PackageManager pm = getPackageManager(); + ResolveInfo resolveInfo = pm.resolveActivity(shortcutInfo.intent, 0); + if ((resolveInfo.activityInfo.applicationInfo.flags & + android.content.pm.ApplicationInfo.FLAG_SYSTEM) != 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(); + } else { + String packageName = shortcutInfo.intent.getComponent().getPackageName(); + String className = shortcutInfo.intent.getComponent().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); + } + } + boolean startActivitySafely(Intent intent, Object tag) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { -- cgit v1.2.3