1fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalpackage com.android.launcher3;
2fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
3fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalimport android.content.ComponentName;
4fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalimport android.content.Context;
5d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyalimport android.content.Intent;
68e0e1d76095badc58a3178917c43642065ace37cSunny Goyalimport android.content.pm.ApplicationInfo;
73e9be43b6ea75c8b82b57aa58508a0c3e8e1d721Sunny Goyalimport android.content.pm.LauncherActivityInfo;
8f2db25398a029b6f13afccbec331e35c8007dde6Sunny Goyalimport android.content.pm.PackageManager;
9d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyalimport android.net.Uri;
10fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalimport android.os.Bundle;
117c74e4ae641e76f73d74348e293c244a157f6585Sunny Goyalimport android.os.UserHandle;
12fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalimport android.os.UserManager;
13fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalimport android.util.AttributeSet;
14c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Mirandaimport android.util.Log;
15d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyalimport android.widget.Toast;
16aa8ef119f18864f4ab41c12f9c2ad6d7f643a0a9Sunny Goyal
178e0e1d76095badc58a3178917c43642065ace37cSunny Goyalimport com.android.launcher3.compat.LauncherAppsCompat;
18fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
19c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Mirandaimport java.net.URISyntaxException;
20c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda
21fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyalpublic class UninstallDropTarget extends ButtonDropTarget {
22fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
23c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda    private static final String TAG = "UninstallDropTarget";
246b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal    private static Boolean sUninstallDisabled;
25c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda
26fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    public UninstallDropTarget(Context context, AttributeSet attrs) {
27fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        this(context, attrs, 0);
28fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
29fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
30fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
31fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        super(context, attrs, defStyle);
32fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
33fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
34fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    @Override
35fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    protected void onFinishInflate() {
36fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        super.onFinishInflate();
37da1dfa32d0dd56c8c9b4667ebd75d847329285b6Sunny Goyal        setupUi();
38da1dfa32d0dd56c8c9b4667ebd75d847329285b6Sunny Goyal    }
39da1dfa32d0dd56c8c9b4667ebd75d847329285b6Sunny Goyal
40da1dfa32d0dd56c8c9b4667ebd75d847329285b6Sunny Goyal    protected void setupUi() {
41fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        // Get the hover color
423a2698b3087c027b81a99a5d429cb0c07977029aSunny Goyal        mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
43da1dfa32d0dd56c8c9b4667ebd75d847329285b6Sunny Goyal        setDrawable(R.drawable.ic_uninstall_shadow);
44fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
45fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
46fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    @Override
47aa8ef119f18864f4ab41c12f9c2ad6d7f643a0a9Sunny Goyal    protected boolean supportsDrop(DragSource source, ItemInfo info) {
481a70cef9884270f2f0a760f079a10fdfb1544c98Sunny Goyal        return supportsDrop(getContext(), info);
491a70cef9884270f2f0a760f079a10fdfb1544c98Sunny Goyal    }
501a70cef9884270f2f0a760f079a10fdfb1544c98Sunny Goyal
511fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal    public static boolean supportsDrop(Context context, ItemInfo info) {
526b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal        if (sUninstallDisabled == null) {
536b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
546b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            Bundle restrictions = userManager.getUserRestrictions();
556b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
566b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal                    || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
576b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal        }
586b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal        if (sUninstallDisabled) {
59a52ecb0390c85afb385371bb844bb496c59ddf87Sunny Goyal            return false;
60fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        }
61fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
626b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal        if (info instanceof AppInfo) {
636b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            AppInfo appInfo = (AppInfo) info;
646b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            if (appInfo.isSystemApp != AppInfo.FLAG_SYSTEM_UNKNOWN) {
656b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal                return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
666b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal            }
676b0aa870b821b5531f6eec782542a2b1e0f2c0eeSunny Goyal        }
688e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        return getUninstallTarget(context, info) != null;
69fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
70fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
71fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    /**
728e0e1d76095badc58a3178917c43642065ace37cSunny Goyal     * @return the component name that should be uninstalled or null.
73fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal     */
741fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal    private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
758e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        Intent intent = null;
767c74e4ae641e76f73d74348e293c244a157f6585Sunny Goyal        UserHandle user = null;
771fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal        if (item != null &&
781fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal                item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
791fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal            intent = item.getIntent();
801fe0c2ca085a284936ac04fdb32b0cd9b82e9c4dSunny Goyal            user = item.user;
818e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        }
828e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        if (intent != null) {
833e9be43b6ea75c8b82b57aa58508a0c3e8e1d721Sunny Goyal            LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
848e0e1d76095badc58a3178917c43642065ace37cSunny Goyal                    .resolveActivity(intent, user);
858e0e1d76095badc58a3178917c43642065ace37cSunny Goyal            if (info != null
868e0e1d76095badc58a3178917c43642065ace37cSunny Goyal                    && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
878e0e1d76095badc58a3178917c43642065ace37cSunny Goyal                return info.getComponentName();
88fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal            }
89fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        }
90fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        return null;
91fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
92fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
93fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    @Override
94fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    public void onDrop(DragObject d) {
95fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        // Differ item deletion
96d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        if (d.dragSource instanceof DropTargetSource) {
97d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
98fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        }
99fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        super.onDrop(d);
100fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
101fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
102fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    @Override
1030f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal    public void completeDrop(final DragObject d) {
104d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
105d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal                ? (DropTargetResultCallback) d.dragSource : null;
106d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        startUninstallActivity(mLauncher, d.dragInfo, callback);
107d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    }
108d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal
109d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
110d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        return startUninstallActivity(launcher, info, null);
111d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    }
112d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal
113d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    public static boolean startUninstallActivity(
114d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
1158e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        final ComponentName cn = getUninstallTarget(launcher, info);
116d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal
117c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda        boolean canUninstall;
1188e0e1d76095badc58a3178917c43642065ace37cSunny Goyal        if (cn == null) {
119d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            // System applications cannot be installed. For now, show a toast explaining that.
120d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            // We may give them the option of disabling apps this way.
121d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
122c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda            canUninstall = false;
123d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        } else {
124c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda            try {
125c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
126c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                        .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
127c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                        .putExtra(Intent.EXTRA_USER, info.user);
128c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                launcher.startActivity(i);
129c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                canUninstall = true;
130c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda            } catch (URISyntaxException e) {
131c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
132c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda                canUninstall = false;
133c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda            }
134d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        }
135d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        if (callback != null) {
136c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda            sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
137d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        }
138c56e3ff2ee03d1756ff70574421e497c5d2f0331Jon Miranda        return canUninstall;
139d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    }
140fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
141d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    /**
142d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     * Notifies the {@param callback} whether the uninstall was successful or not.
143d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     *
144d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     * Since there is no direct callback for an uninstall request, we check the package existence
145d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     * when the launch resumes next time. This assumes that the uninstall activity will finish only
146d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     * after the task is completed
147d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal     */
148d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    protected static void sendUninstallResult(
149d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            final Launcher launcher, boolean activityStarted,
1507c74e4ae641e76f73d74348e293c244a157f6585Sunny Goyal            final ComponentName cn, final UserHandle user,
151d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            final DropTargetResultCallback callback) {
152d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        if (activityStarted)  {
153fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal            final Runnable checkIfUninstallWasSuccess = new Runnable() {
154fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal                @Override
155fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal                public void run() {
156f2db25398a029b6f13afccbec331e35c8007dde6Sunny Goyal                    // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
157f2db25398a029b6f13afccbec331e35c8007dde6Sunny Goyal                    boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher)
158f2db25398a029b6f13afccbec331e35c8007dde6Sunny Goyal                            .getApplicationInfo(cn.getPackageName(),
159f2db25398a029b6f13afccbec331e35c8007dde6Sunny Goyal                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null;
160d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal                    callback.onDragObjectRemoved(uninstallSuccessful);
161fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal                }
162fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal            };
163d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
164fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        } else {
165d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal            callback.onDragObjectRemoved(false);
166fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        }
167fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
168fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
169d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    public interface DropTargetResultCallback {
170d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        /**
171d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal         * A drag operation was complete.
172d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal         * @param isRemoved true if the drag object should be removed, false otherwise.
173d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal         */
174d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal        void onDragObjectRemoved(boolean isRemoved);
175fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
176fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
177fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    /**
178fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal     * Interface defining an object that can provide uninstallable drag objects.
179fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal     */
180d5bd67dfa9ee5fda2384a75231b7a68ceb8e9bd5Sunny Goyal    public interface DropTargetSource extends DropTargetResultCallback {
181fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal
182fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        /**
183fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal         * Indicates that an uninstall request are made and the actual result may come
184fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal         * after some time.
185fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal         */
186fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal        void deferCompleteDropAfterUninstallActivity();
187fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal    }
188fa401a10e7e9341daf6f3c5949bf9331902c26d0Sunny Goyal}
189