Adding support for result callback when starting uninstall-application activity

Change-Id: Ieaca4fbd0ae0156f24c8863ccbef61d4d6d30ba1
This commit is contained in:
Sunny Goyal
2016-03-11 01:10:19 -08:00
parent d3cc05a3a8
commit d5bd67dfa9
6 changed files with 109 additions and 99 deletions
+31 -10
View File
@@ -16,12 +16,19 @@
package com.android.launcher3; package com.android.launcher3;
import android.content.ActivityNotFoundException;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.widget.Toast;
import com.android.launcher3.compat.LauncherAppsCompat;
public class InfoDropTarget extends UninstallDropTarget { public class InfoDropTarget extends UninstallDropTarget {
private static final String TAG = "InfoDropTarget";
public InfoDropTarget(Context context, AttributeSet attrs) { public InfoDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0); this(context, attrs, 0);
} }
@@ -39,10 +46,19 @@ public class InfoDropTarget extends UninstallDropTarget {
setDrawable(R.drawable.ic_info_launcher); setDrawable(R.drawable.ic_info_launcher);
} }
@Override
void completeDrop(DragObject d) {
DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
? (DropTargetResultCallback) d.dragSource : null;
startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
}
/** /**
* @return Whether the activity was started. * @return Whether the activity was started.
*/ */
public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher) { public static boolean startDetailsActivityForInfo(
ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
boolean result = false;
ComponentName componentName = null; ComponentName componentName = null;
if (info instanceof AppInfo) { if (info instanceof AppInfo) {
componentName = ((AppInfo) info).componentName; componentName = ((AppInfo) info).componentName;
@@ -54,23 +70,28 @@ public class InfoDropTarget extends UninstallDropTarget {
componentName = ((LauncherAppWidgetInfo) info).providerName; componentName = ((LauncherAppWidgetInfo) info).providerName;
} }
if (componentName != null) { if (componentName != null) {
launcher.startApplicationDetailsActivity(componentName, info.user); try {
return true; LauncherAppsCompat.getInstance(launcher)
.showAppDetailsForProfile(componentName, info.user);
result = true;
} catch (SecurityException | ActivityNotFoundException e) {
Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch settings", e);
}
} }
return false;
}
@Override if (callback != null) {
protected boolean startActivityWithUninstallAffordance(DragObject d) { sendUninstallResult(launcher, result, componentName, info.user, callback);
return startDetailsActivityForInfo(d.dragInfo, mLauncher); }
return result;
} }
@Override @Override
protected boolean supportsDrop(DragSource source, ItemInfo info) { protected boolean supportsDrop(DragSource source, ItemInfo info) {
return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); return source.supportsAppInfoDropTarget() && supportsDrop(info);
} }
public static boolean supportsDrop(Context context, ItemInfo info) { public static boolean supportsDrop(ItemInfo info) {
return info instanceof AppInfo || info instanceof ShortcutInfo return info instanceof AppInfo || info instanceof ShortcutInfo
|| info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo; || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo;
} }
+1 -39
View File
@@ -57,7 +57,6 @@ import android.graphics.PorterDuff;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@@ -108,9 +107,9 @@ import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.logging.UserEventLogger; import com.android.launcher3.logging.UserEventLogger;
import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.LongArrayMap;
@@ -2828,43 +2827,6 @@ public class Launcher extends Activity
} }
} }
void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
try {
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
launcherApps.showAppDetailsForProfile(componentName, user);
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have permission to launch settings");
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch settings");
}
}
// returns true if the activity was started
boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
UserHandleCompat user) {
if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
int messageId = R.string.uninstall_system_app_text;
Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
return false;
} else {
String packageName = componentName.getPackageName();
String className = componentName.getClassName();
Intent intent = new Intent(
Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (user != null) {
user.addToIntent(intent, Intent.EXTRA_USER);
}
startActivity(intent);
return true;
}
}
private boolean startActivity(View v, Intent intent, Object tag) { private boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try { try {
@@ -3,14 +3,16 @@ package com.android.launcher3;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserManager; import android.os.UserManager;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair; import android.util.Pair;
import android.widget.Toast;
import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.Thunk;
public class UninstallDropTarget extends ButtonDropTarget { public class UninstallDropTarget extends ButtonDropTarget {
@@ -72,63 +74,89 @@ public class UninstallDropTarget extends ButtonDropTarget {
@Override @Override
public void onDrop(DragObject d) { public void onDrop(DragObject d) {
// Differ item deletion // Differ item deletion
if (d.dragSource instanceof UninstallSource) { if (d.dragSource instanceof DropTargetSource) {
((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity(); ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
} }
super.onDrop(d); super.onDrop(d);
} }
@Override @Override
void completeDrop(final DragObject d) { void completeDrop(final DragObject d) {
final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo); DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
final UserHandleCompat user = d.dragInfo.user; ? (DropTargetResultCallback) d.dragSource : null;
if (startActivityWithUninstallAffordance(d)) { startUninstallActivity(mLauncher, d.dragInfo, callback);
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
boolean uninstallSuccessful = false;
if (componentInfo != null) {
String packageName = componentInfo.first.getPackageName();
uninstallSuccessful = !AllAppsList.packageHasActivities(
getContext(), packageName, user);
}
sendUninstallResult(d.dragSource, uninstallSuccessful);
}
};
mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
} else {
sendUninstallResult(d.dragSource, false);
}
}
protected boolean startActivityWithUninstallAffordance(DragObject d) {
return startUninstallActivity(mLauncher, d.dragInfo);
} }
public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info); return startUninstallActivity(launcher, info, null);
final UserHandleCompat user = info.user;
return launcher.startApplicationUninstallActivity(
componentInfo.first, componentInfo.second, user);
} }
@Thunk void sendUninstallResult(DragSource target, boolean result) { public static boolean startUninstallActivity(
if (target instanceof UninstallSource) { final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
((UninstallSource) target).onUninstallActivityReturned(result); Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
ComponentName cn = componentInfo.first;
final boolean isUninstallable;
if ((componentInfo.second & AppInfo.DOWNLOADED_FLAG) == 0) {
// 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);
info.user.addToIntent(intent, Intent.EXTRA_USER);
launcher.startActivity(intent);
isUninstallable = true;
} }
if (callback != null) {
sendUninstallResult(
launcher, isUninstallable, componentInfo.first, 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 UserHandleCompat user,
final DropTargetResultCallback callback) {
if (activityStarted) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
String packageName = cn.getPackageName();
boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
launcher, packageName, user);
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. * Interface defining an object that can provide uninstallable drag objects.
*/ */
public interface UninstallSource { public interface DropTargetSource extends DropTargetResultCallback {
/**
* A pending uninstall operation was complete.
* @param result true if uninstall was successful, false otherwise.
*/
void onUninstallActivityReturned(boolean result);
/** /**
* Indicates that an uninstall request are made and the actual result may come * Indicates that an uninstall request are made and the actual result may come
+3 -3
View File
@@ -57,7 +57,7 @@ import android.widget.TextView;
import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
@@ -92,7 +92,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class Workspace extends PagedView public class Workspace extends PagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener, implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider { Insettable, DropTargetSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
private static final String TAG = "Launcher.Workspace"; private static final String TAG = "Launcher.Workspace";
private static boolean ENFORCE_DRAG_EVENT_ORDER = false; private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
@@ -3664,7 +3664,7 @@ public class Workspace extends PagedView
/// maybe move this into a smaller part /// maybe move this into a smaller part
@Override @Override
public void onUninstallActivityReturned(boolean success) { public void onDragObjectRemoved(boolean success) {
mDeferDropAfterUninstall = false; mDeferDropAfterUninstall = false;
mUninstallSuccessful = success; mUninstallSuccessful = success;
if (mDeferredAction != null) { if (mDeferredAction != null) {
@@ -102,7 +102,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
if (UninstallDropTarget.supportsDrop(host.getContext(), item)) { if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
info.addAction(mActions.get(UNINSTALL)); info.addAction(mActions.get(UNINSTALL));
} }
if (InfoDropTarget.supportsDrop(host.getContext(), item)) { if (InfoDropTarget.supportsDrop(item)) {
info.addAction(mActions.get(INFO)); info.addAction(mActions.get(INFO));
} }
@@ -137,7 +137,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host); DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
return true; return true;
} else if (action == INFO) { } else if (action == INFO) {
InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null);
return true; return true;
} else if (action == UNINSTALL) { } else if (action == UNINSTALL) {
return UninstallDropTarget.startUninstallActivity(mLauncher, item); return UninstallDropTarget.startUninstallActivity(mLauncher, item);
+3 -4
View File
@@ -71,9 +71,8 @@ import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo; import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Stats; import com.android.launcher3.Stats;
import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
@@ -92,7 +91,7 @@ import java.util.Comparator;
*/ */
public class Folder extends LinearLayout implements DragSource, View.OnClickListener, public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource, View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource,
Stats.LaunchSourceProvider { Stats.LaunchSourceProvider {
private static final String TAG = "Launcher.Folder"; private static final String TAG = "Launcher.Folder";
@@ -980,7 +979,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
} }
@Override @Override
public void onUninstallActivityReturned(boolean success) { public void onDragObjectRemoved(boolean success) {
mDeferDropAfterUninstall = false; mDeferDropAfterUninstall = false;
mUninstallSuccessful = success; mUninstallSuccessful = success;
if (mDeferredAction != null) { if (mDeferredAction != null) {