package com.android.launcher3; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.UserManager; import android.util.AttributeSet; import android.util.Pair; import android.widget.Toast; import com.android.launcher3.compat.UserHandleCompat; public class UninstallDropTarget extends ButtonDropTarget { public UninstallDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onFinishInflate() { super.onFinishInflate(); // Get the hover color mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint); setDrawable(R.drawable.ic_uninstall_launcher); } @Override protected boolean supportsDrop(DragSource source, ItemInfo info) { return supportsDrop(getContext(), info); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public static boolean supportsDrop(Context context, Object info) { if (Utilities.ATLEAST_JB_MR2) { UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); Bundle restrictions = userManager.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) { return false; } } Pair componentInfo = getAppInfoFlags(info); return componentInfo != null && (componentInfo.second & AppInfo.DOWNLOADED_FLAG) != 0; } /** * @return the component name and flags if {@param info} is an AppInfo or an app shortcut. */ private static Pair getAppInfoFlags(Object item) { if (item instanceof AppInfo) { AppInfo info = (AppInfo) item; return Pair.create(info.componentName, info.flags); } else if (item instanceof ShortcutInfo) { ShortcutInfo info = (ShortcutInfo) item; ComponentName component = info.getTargetComponent(); if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION && component != null) { return Pair.create(component, info.flags); } } return null; } @Override public void onDrop(DragObject d) { // Differ item deletion if (d.dragSource instanceof DropTargetSource) { ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity(); } super.onDrop(d); } @Override void completeDrop(final DragObject d) { DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback ? (DropTargetResultCallback) d.dragSource : null; startUninstallActivity(mLauncher, d.dragInfo, callback); } public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { return startUninstallActivity(launcher, info, null); } public static boolean startUninstallActivity( final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) { Pair componentInfo = getAppInfoFlags(info); ComponentName cn = componentInfo.first; final boolean isUninstallable; if ((componentInfo.second & AppInfo.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. Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); isUninstallable = false; } else { Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); info.user.addToIntent(intent, Intent.EXTRA_USER); launcher.startActivity(intent); isUninstallable = true; } if (callback != null) { sendUninstallResult( launcher, isUninstallable, componentInfo.first, info.user, callback); } return isUninstallable; } /** * Notifies the {@param callback} whether the uninstall was successful or not. * * Since there is no direct callback for an uninstall request, we check the package existence * when the launch resumes next time. This assumes that the uninstall activity will finish only * after the task is completed */ protected static void sendUninstallResult( final Launcher launcher, boolean activityStarted, final ComponentName cn, final UserHandleCompat user, final DropTargetResultCallback callback) { if (activityStarted) { final Runnable checkIfUninstallWasSuccess = new Runnable() { @Override public void run() { String packageName = cn.getPackageName(); boolean uninstallSuccessful = !AllAppsList.packageHasActivities( launcher, packageName, user); callback.onDragObjectRemoved(uninstallSuccessful); } }; launcher.addOnResumeCallback(checkIfUninstallWasSuccess); } else { callback.onDragObjectRemoved(false); } } public interface DropTargetResultCallback { /** * A drag operation was complete. * @param isRemoved true if the drag object should be removed, false otherwise. */ void onDragObjectRemoved(boolean isRemoved); } /** * Interface defining an object that can provide uninstallable drag objects. */ public interface DropTargetSource extends DropTargetResultCallback { /** * Indicates that an uninstall request are made and the actual result may come * after some time. */ void deferCompleteDropAfterUninstallActivity(); } }