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