UninstallDropTarget.java revision d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5
1package com.android.launcher3; 2 3import android.annotation.TargetApi; 4import android.content.ComponentName; 5import android.content.Context; 6import android.content.Intent; 7import android.net.Uri; 8import android.os.Build; 9import android.os.Bundle; 10import android.os.UserManager; 11import android.util.AttributeSet; 12import android.util.Pair; 13import android.widget.Toast; 14 15import com.android.launcher3.compat.UserHandleCompat; 16 17public class UninstallDropTarget extends ButtonDropTarget { 18 19 public UninstallDropTarget(Context context, AttributeSet attrs) { 20 this(context, attrs, 0); 21 } 22 23 public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) { 24 super(context, attrs, defStyle); 25 } 26 27 @Override 28 protected void onFinishInflate() { 29 super.onFinishInflate(); 30 // Get the hover color 31 mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint); 32 33 setDrawable(R.drawable.ic_uninstall_launcher); 34 } 35 36 @Override 37 protected boolean supportsDrop(DragSource source, ItemInfo info) { 38 return supportsDrop(getContext(), info); 39 } 40 41 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) 42 public static boolean supportsDrop(Context context, Object info) { 43 if (Utilities.ATLEAST_JB_MR2) { 44 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 45 Bundle restrictions = userManager.getUserRestrictions(); 46 if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) 47 || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) { 48 return false; 49 } 50 } 51 52 Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info); 53 return componentInfo != null && (componentInfo.second & AppInfo.DOWNLOADED_FLAG) != 0; 54 } 55 56 /** 57 * @return the component name and flags if {@param info} is an AppInfo or an app shortcut. 58 */ 59 private static Pair<ComponentName, Integer> getAppInfoFlags(Object item) { 60 if (item instanceof AppInfo) { 61 AppInfo info = (AppInfo) item; 62 return Pair.create(info.componentName, info.flags); 63 } else if (item instanceof ShortcutInfo) { 64 ShortcutInfo info = (ShortcutInfo) item; 65 ComponentName component = info.getTargetComponent(); 66 if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION 67 && component != null) { 68 return Pair.create(component, info.flags); 69 } 70 } 71 return null; 72 } 73 74 @Override 75 public void onDrop(DragObject d) { 76 // Differ item deletion 77 if (d.dragSource instanceof DropTargetSource) { 78 ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity(); 79 } 80 super.onDrop(d); 81 } 82 83 @Override 84 void completeDrop(final DragObject d) { 85 DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback 86 ? (DropTargetResultCallback) d.dragSource : null; 87 startUninstallActivity(mLauncher, d.dragInfo, callback); 88 } 89 90 public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { 91 return startUninstallActivity(launcher, info, null); 92 } 93 94 public static boolean startUninstallActivity( 95 final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) { 96 Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info); 97 ComponentName cn = componentInfo.first; 98 99 final boolean isUninstallable; 100 if ((componentInfo.second & AppInfo.DOWNLOADED_FLAG) == 0) { 101 // System applications cannot be installed. For now, show a toast explaining that. 102 // We may give them the option of disabling apps this way. 103 Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); 104 isUninstallable = false; 105 } else { 106 Intent intent = new Intent(Intent.ACTION_DELETE, 107 Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) 108 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 109 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 110 info.user.addToIntent(intent, Intent.EXTRA_USER); 111 launcher.startActivity(intent); 112 isUninstallable = true; 113 } 114 if (callback != null) { 115 sendUninstallResult( 116 launcher, isUninstallable, componentInfo.first, info.user, callback); 117 } 118 return isUninstallable; 119 } 120 121 /** 122 * Notifies the {@param callback} whether the uninstall was successful or not. 123 * 124 * Since there is no direct callback for an uninstall request, we check the package existence 125 * when the launch resumes next time. This assumes that the uninstall activity will finish only 126 * after the task is completed 127 */ 128 protected static void sendUninstallResult( 129 final Launcher launcher, boolean activityStarted, 130 final ComponentName cn, final UserHandleCompat user, 131 final DropTargetResultCallback callback) { 132 if (activityStarted) { 133 final Runnable checkIfUninstallWasSuccess = new Runnable() { 134 @Override 135 public void run() { 136 String packageName = cn.getPackageName(); 137 boolean uninstallSuccessful = !AllAppsList.packageHasActivities( 138 launcher, packageName, user); 139 callback.onDragObjectRemoved(uninstallSuccessful); 140 } 141 }; 142 launcher.addOnResumeCallback(checkIfUninstallWasSuccess); 143 } else { 144 callback.onDragObjectRemoved(false); 145 } 146 } 147 148 public interface DropTargetResultCallback { 149 /** 150 * A drag operation was complete. 151 * @param isRemoved true if the drag object should be removed, false otherwise. 152 */ 153 void onDragObjectRemoved(boolean isRemoved); 154 } 155 156 /** 157 * Interface defining an object that can provide uninstallable drag objects. 158 */ 159 public interface DropTargetSource extends DropTargetResultCallback { 160 161 /** 162 * Indicates that an uninstall request are made and the actual result may come 163 * after some time. 164 */ 165 void deferCompleteDropAfterUninstallActivity(); 166 } 167} 168