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