blob: 3f7de0604b7ba0c46f8b6b231fcdb5ab5e8bbf71 [file] [log] [blame]
Sunny Goyalfa401a12015-04-10 13:45:42 -07001package com.android.launcher3;
2
3import android.content.ComponentName;
4import android.content.Context;
Sunny Goyald5bd67d2016-03-11 01:10:19 -08005import android.content.Intent;
Sunny Goyal8e0e1d72016-10-10 10:41:41 -07006import android.content.pm.ApplicationInfo;
Sunny Goyal3e9be432017-01-05 15:22:41 -08007import android.content.pm.LauncherActivityInfo;
Sunny Goyalf2db2532017-03-01 17:27:16 -08008import android.content.pm.PackageManager;
Sunny Goyald5bd67d2016-03-11 01:10:19 -08009import android.net.Uri;
Sunny Goyalfa401a12015-04-10 13:45:42 -070010import android.os.Bundle;
Sunny Goyal7c74e4a2016-12-15 15:53:17 -080011import android.os.UserHandle;
Sunny Goyalfa401a12015-04-10 13:45:42 -070012import android.os.UserManager;
13import android.util.AttributeSet;
Jon Mirandac56e3ff2017-08-23 12:13:24 -070014import android.util.Log;
Sunny Goyald5bd67d2016-03-11 01:10:19 -080015import android.widget.Toast;
Sunny Goyalaa8ef112015-06-12 20:04:41 -070016
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070017import com.android.launcher3.compat.LauncherAppsCompat;
Sunny Goyalfa401a12015-04-10 13:45:42 -070018
Jon Mirandac56e3ff2017-08-23 12:13:24 -070019import java.net.URISyntaxException;
20
Sunny Goyalfa401a12015-04-10 13:45:42 -070021public class UninstallDropTarget extends ButtonDropTarget {
22
Jon Mirandac56e3ff2017-08-23 12:13:24 -070023 private static final String TAG = "UninstallDropTarget";
Sunny Goyal6b0aa872017-09-29 07:54:37 -070024 private static Boolean sUninstallDisabled;
Jon Mirandac56e3ff2017-08-23 12:13:24 -070025
Sunny Goyalfa401a12015-04-10 13:45:42 -070026 public UninstallDropTarget(Context context, AttributeSet attrs) {
27 this(context, attrs, 0);
28 }
29
30 public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
31 super(context, attrs, defStyle);
32 }
33
34 @Override
35 protected void onFinishInflate() {
36 super.onFinishInflate();
Sunny Goyalda1dfa32017-04-26 22:34:49 -070037 setupUi();
38 }
39
40 protected void setupUi() {
Sunny Goyalfa401a12015-04-10 13:45:42 -070041 // Get the hover color
Sunny Goyal3a2698b2015-04-28 21:36:46 -070042 mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
Sunny Goyalda1dfa32017-04-26 22:34:49 -070043 setDrawable(R.drawable.ic_uninstall_shadow);
Sunny Goyalfa401a12015-04-10 13:45:42 -070044 }
45
46 @Override
Sunny Goyal1ce9c472017-10-03 16:17:32 -070047 protected boolean supportsDrop(ItemInfo info) {
Sunny Goyal1a70cef2015-04-22 11:29:51 -070048 return supportsDrop(getContext(), info);
49 }
50
Sunny Goyal1fe0c2c2017-08-15 12:54:42 -070051 public static boolean supportsDrop(Context context, ItemInfo info) {
Sunny Goyal6b0aa872017-09-29 07:54:37 -070052 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) {
Sunny Goyala52ecb02016-12-16 15:04:51 -080059 return false;
Sunny Goyalfa401a12015-04-10 13:45:42 -070060 }
61
Sunny Goyal6b0aa872017-09-29 07:54:37 -070062 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 }
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070068 return getUninstallTarget(context, info) != null;
Sunny Goyalfa401a12015-04-10 13:45:42 -070069 }
70
71 /**
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070072 * @return the component name that should be uninstalled or null.
Sunny Goyalfa401a12015-04-10 13:45:42 -070073 */
Sunny Goyal1fe0c2c2017-08-15 12:54:42 -070074 private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070075 Intent intent = null;
Sunny Goyal7c74e4a2016-12-15 15:53:17 -080076 UserHandle user = null;
Sunny Goyal1fe0c2c2017-08-15 12:54:42 -070077 if (item != null &&
78 item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
79 intent = item.getIntent();
80 user = item.user;
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070081 }
82 if (intent != null) {
Sunny Goyal3e9be432017-01-05 15:22:41 -080083 LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
Sunny Goyal8e0e1d72016-10-10 10:41:41 -070084 .resolveActivity(intent, user);
85 if (info != null
86 && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
87 return info.getComponentName();
Sunny Goyalfa401a12015-04-10 13:45:42 -070088 }
89 }
90 return null;
91 }
92
93 @Override
94 public void onDrop(DragObject d) {
95 // Differ item deletion
Sunny Goyald5bd67d2016-03-11 01:10:19 -080096 if (d.dragSource instanceof DropTargetSource) {
97 ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
Sunny Goyalfa401a12015-04-10 13:45:42 -070098 }
99 super.onDrop(d);
100 }
101
102 @Override
Sunny Goyal0f76b562016-12-13 19:37:10 -0800103 public void completeDrop(final DragObject d) {
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800104 DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
105 ? (DropTargetResultCallback) d.dragSource : null;
106 startUninstallActivity(mLauncher, d.dragInfo, callback);
Tony Wickham734dfbe2015-09-16 16:22:36 -0700107 }
108
Sunny Goyalaa8ef112015-06-12 20:04:41 -0700109 public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800110 return startUninstallActivity(launcher, info, null);
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700111 }
112
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800113 public static boolean startUninstallActivity(
114 final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
Sunny Goyal8e0e1d72016-10-10 10:41:41 -0700115 final ComponentName cn = getUninstallTarget(launcher, info);
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800116
Jon Mirandac56e3ff2017-08-23 12:13:24 -0700117 boolean canUninstall;
Sunny Goyal8e0e1d72016-10-10 10:41:41 -0700118 if (cn == null) {
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800119 // 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();
Jon Mirandac56e3ff2017-08-23 12:13:24 -0700122 canUninstall = false;
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800123 } else {
Jon Mirandac56e3ff2017-08-23 12:13:24 -0700124 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 }
Sunny Goyalfa401a12015-04-10 13:45:42 -0700134 }
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800135 if (callback != null) {
Jon Mirandac56e3ff2017-08-23 12:13:24 -0700136 sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800137 }
Jon Mirandac56e3ff2017-08-23 12:13:24 -0700138 return canUninstall;
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800139 }
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 */
148 protected static void sendUninstallResult(
149 final Launcher launcher, boolean activityStarted,
Sunny Goyal7c74e4a2016-12-15 15:53:17 -0800150 final ComponentName cn, final UserHandle user,
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800151 final DropTargetResultCallback callback) {
152 if (activityStarted) {
153 final Runnable checkIfUninstallWasSuccess = new Runnable() {
154 @Override
155 public void run() {
Sunny Goyalf2db2532017-03-01 17:27:16 -0800156 // 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;
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800160 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 */
174 void onDragObjectRemoved(boolean isRemoved);
Sunny Goyalfa401a12015-04-10 13:45:42 -0700175 }
176
177 /**
178 * Interface defining an object that can provide uninstallable drag objects.
179 */
Sunny Goyald5bd67d2016-03-11 01:10:19 -0800180 public interface DropTargetSource extends DropTargetResultCallback {
Sunny Goyalfa401a12015-04-10 13:45:42 -0700181
182 /**
183 * Indicates that an uninstall request are made and the actual result may come
184 * after some time.
185 */
186 void deferCompleteDropAfterUninstallActivity();
187 }
188}