Files
Lawnchair/src/com/android/launcher3/UninstallDropTarget.java
T
Sunny Goyal f2db25398a Fixing wrong check being used for application status
Earlier we were relying on activity list to check is an app is
installed or not. It fails if the app only exposes widgets and
no visible activity. Hence dragging an icon to AppInfo sometimes
causes the icon to get disappear.

Bug: 33241335
Change-Id: Ieb71e298a0930dd0a831cf09bd1e998514a427fd
2017-03-01 17:38:12 -08:00

176 lines
6.6 KiB
Java

package com.android.launcher3;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.widget.Toast;
import com.android.launcher3.compat.LauncherAppsCompat;
public class UninstallDropTarget extends ButtonDropTarget {
public UninstallDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Get the hover color
mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
setDrawable(R.drawable.ic_uninstall_launcher);
}
@Override
protected boolean supportsDrop(DragSource source, ItemInfo info) {
return supportsDrop(getContext(), info);
}
public static boolean supportsDrop(Context context, Object info) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
Bundle restrictions = userManager.getUserRestrictions();
if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
|| restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
return false;
}
return getUninstallTarget(context, info) != null;
}
/**
* @return the component name that should be uninstalled or null.
*/
private static ComponentName getUninstallTarget(Context context, Object item) {
Intent intent = null;
UserHandle user = null;
if (item instanceof AppInfo) {
AppInfo info = (AppInfo) item;
intent = info.intent;
user = info.user;
} else if (item instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) item;
if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
// Do not use restore/target intent here as we cannot uninstall an app which is
// being installed/restored.
intent = info.intent;
user = info.user;
}
}
if (intent != null) {
LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
.resolveActivity(intent, user);
if (info != null
&& (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return info.getComponentName();
}
}
return null;
}
@Override
public void onDrop(DragObject d) {
// Differ item deletion
if (d.dragSource instanceof DropTargetSource) {
((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
}
super.onDrop(d);
}
@Override
public void completeDrop(final DragObject d) {
DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
? (DropTargetResultCallback) d.dragSource : null;
startUninstallActivity(mLauncher, d.dragInfo, callback);
}
public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
return startUninstallActivity(launcher, info, null);
}
public static boolean startUninstallActivity(
final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
final ComponentName cn = getUninstallTarget(launcher, info);
final boolean isUninstallable;
if (cn == null) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
isUninstallable = false;
} else {
Intent intent = new Intent(Intent.ACTION_DELETE,
Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(Intent.EXTRA_USER, info.user);
launcher.startActivity(intent);
isUninstallable = true;
}
if (callback != null) {
sendUninstallResult(launcher, isUninstallable, cn, info.user, callback);
}
return isUninstallable;
}
/**
* Notifies the {@param callback} whether the uninstall was successful or not.
*
* Since there is no direct callback for an uninstall request, we check the package existence
* when the launch resumes next time. This assumes that the uninstall activity will finish only
* after the task is completed
*/
protected static void sendUninstallResult(
final Launcher launcher, boolean activityStarted,
final ComponentName cn, final UserHandle user,
final DropTargetResultCallback callback) {
if (activityStarted) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
// We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher)
.getApplicationInfo(cn.getPackageName(),
PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null;
callback.onDragObjectRemoved(uninstallSuccessful);
}
};
launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
} else {
callback.onDragObjectRemoved(false);
}
}
public interface DropTargetResultCallback {
/**
* A drag operation was complete.
* @param isRemoved true if the drag object should be removed, false otherwise.
*/
void onDragObjectRemoved(boolean isRemoved);
}
/**
* Interface defining an object that can provide uninstallable drag objects.
*/
public interface DropTargetSource extends DropTargetResultCallback {
/**
* Indicates that an uninstall request are made and the actual result may come
* after some time.
*/
void deferCompleteDropAfterUninstallActivity();
}
}