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