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