Updating drop button targets

> Splitting DeleteDropTarget into delete and uninstall
> Showing UninstallDropTarget for app shortcuts on workspace
> Showing InfoDropTarget only when developer options is enabled

Change-Id: I4396571d2199d1581bb9c733aef88ab9b0ebd79d
This commit is contained in:
Sunny Goyal 2015-04-10 13:45:42 -07:00
parent fb9ca23928
commit fa401a10e7
13 changed files with 313 additions and 375 deletions

View File

@ -41,6 +41,19 @@
android:text="@string/delete_target_label" />
</FrameLayout>
<FrameLayout
style="@style/DropTargetButtonContainer"
android:layout_weight="1" >
<!-- Uninstall target -->
<com.android.launcher3.UninstallDropTarget
android:id="@+id/uninstall_target_text"
style="@style/DropTargetButton"
android:drawableStart="@drawable/uninstall_target_selector"
android:text="@string/delete_target_uninstall_label" />
</FrameLayout>
<FrameLayout
style="@style/DropTargetButtonContainer"
android:layout_weight="1" >

View File

@ -34,6 +34,7 @@ import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.util.Thunk;
import java.util.List;
@ -286,7 +287,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
@Override
public boolean supportsDeleteDropTarget() {
return true;
return false;
}
@Override

View File

@ -17,19 +17,29 @@
package com.android.launcher3;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
import com.android.launcher3.R;
import com.android.launcher3.util.Thunk;
/**
* Implements a DropTarget.
*/
public class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener {
public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener {
private static int DRAG_VIEW_DROP_DURATION = 285;
protected final int mTransitionDuration;
@ -44,6 +54,9 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
/** The paint applied to the drag view on hover */
protected int mHoverColor = 0;
protected ColorStateList mOriginalTextColor;
protected TransitionDrawable mDrawable;
public ButtonDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@ -56,12 +69,37 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
mBottomDragPadding = r.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
}
void setLauncher(Launcher launcher) {
mLauncher = launcher;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mOriginalTextColor = getTextColors();
// Remove the text in the Phone UI in landscape
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (!LauncherAppState.getInstance().isScreenLarge()) {
setText("");
}
}
}
public boolean acceptDrop(DragObject d) {
return false;
protected void setDrawable(int resId) {
// Get the hover color
mDrawable = (TransitionDrawable) getCurrentDrawable();
if (mDrawable == null) {
// TODO: investigate why this is ever happening. Presently only on one known device.
mDrawable = (TransitionDrawable) getResources().getDrawable(resId);
setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
}
if (null != mDrawable) {
mDrawable.setCrossFadeEnabled(true);
}
}
public void setLauncher(Launcher launcher) {
mLauncher = launcher;
}
public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) {
@ -78,37 +116,94 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
return null;
}
public void onDrop(DragObject d) {
}
@Override
public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { }
public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
// Do nothing
}
public void onDragEnter(DragObject d) {
@Override
public final void onDragEnter(DragObject d) {
d.dragView.setColor(mHoverColor);
mDrawable.startTransition(mTransitionDuration);
setTextColor(mHoverColor);
}
@Override
public void onDragOver(DragObject d) {
// Do nothing
}
public void onDragExit(DragObject d) {
d.dragView.setColor(0);
protected void resetHoverColor() {
mDrawable.resetTransition();
setTextColor(mOriginalTextColor);
}
public void onDragStart(DragSource source, Object info, int dragAction) {
// Do nothing
@Override
public final void onDragExit(DragObject d) {
if (!d.dragComplete) {
d.dragView.setColor(0);
resetHoverColor();
} else {
// Restore the hover color
d.dragView.setColor(mHoverColor);
}
}
@Override
public final void onDragStart(DragSource source, Object info, int dragAction) {
mActive = supportsDrop(source, info);
mDrawable.resetTransition();
setTextColor(mOriginalTextColor);
((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE);
}
@Override
public final boolean acceptDrop(DragObject dragObject) {
return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
}
protected abstract boolean supportsDrop(DragSource source, Object info);
@Override
public boolean isDropEnabled() {
return mActive;
}
@Override
public void onDragEnd() {
// Do nothing
mActive = false;
}
/**
* On drop animate the dropView to the icon.
*/
@Override
public void onDrop(final DragObject d) {
final DragLayer dragLayer = mLauncher.getDragLayer();
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
width, height);
final float scale = (float) to.width() / from.width();
mSearchDropTargetBar.deferOnDragEnd();
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
completeDrop(d);
mSearchDropTargetBar.onDragEnd();
mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
new LinearInterpolator(), onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
@Thunk abstract void completeDrop(DragObject d);
@Override
public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
super.getHitRect(outRect);
@ -120,10 +215,10 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
}
private boolean isRtl() {
return (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
DragLayer dragLayer = mLauncher.getDragLayer();
// Find the rect to animate to (the view is center aligned)
@ -157,6 +252,7 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
return to;
}
@Override
public void getLocationInDragLayer(int[] loc) {
mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
}

View File

@ -19,33 +19,22 @@ package com.android.launcher3;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.R;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
public class DeleteDropTarget extends ButtonDropTarget {
private static int DELETE_ANIMATION_DURATION = 285;
private static int FLING_DELETE_ANIMATION_DURATION = 350;
private static float FLING_TO_DELETE_FRICTION = 0.035f;
private static int MODE_FLING_DELETE_TO_TRASH = 0;
@ -53,13 +42,6 @@ public class DeleteDropTarget extends ButtonDropTarget {
private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
private ColorStateList mOriginalTextColor;
private TransitionDrawable mUninstallDrawable;
private TransitionDrawable mRemoveDrawable;
private TransitionDrawable mCurrentDrawable;
@Thunk boolean mWaitingForUninstall = false;
public DeleteDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@ -71,258 +53,27 @@ public class DeleteDropTarget extends ButtonDropTarget {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Get the drawable
mOriginalTextColor = getTextColors();
// Get the hover color
Resources r = getResources();
mHoverColor = r.getColor(R.color.delete_target_hover_tint);
mUninstallDrawable = (TransitionDrawable)
r.getDrawable(R.drawable.uninstall_target_selector);
mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector);
mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
mRemoveDrawable.setCrossFadeEnabled(true);
mUninstallDrawable.setCrossFadeEnabled(true);
// The current drawable is set to either the remove drawable or the uninstall drawable
// and is initially set to the remove drawable, as set in the layout xml.
mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
// Remove the text in the Phone UI in landscape
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (!LauncherAppState.getInstance().isScreenLarge()) {
setText("");
}
}
setDrawable(R.drawable.remove_target_selector);
}
private boolean isAllAppsApplication(DragSource source, Object info) {
return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
}
private boolean isWidget(DragSource source, Object info) {
if (source instanceof WidgetsContainerView) {
if (info instanceof PendingAddItemInfo) {
PendingAddItemInfo addInfo = (PendingAddItemInfo) info;
switch (addInfo.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
return true;
}
}
}
return false;
}
private boolean isDragSourceWorkspaceOrFolder(DragObject d) {
return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder);
}
private void setHoverColor() {
if (mCurrentDrawable != null) {
mCurrentDrawable.startTransition(mTransitionDuration);
}
setTextColor(mHoverColor);
}
private void resetHoverColor() {
if (mCurrentDrawable != null) {
mCurrentDrawable.resetTransition();
}
setTextColor(mOriginalTextColor);
public static boolean willAcceptDrop(DragSource source, Object info) {
return (info instanceof ItemInfo) && source.supportsDeleteDropTarget();
}
@Override
public boolean acceptDrop(DragObject d) {
return willAcceptDrop(d.dragInfo);
}
public static boolean willAcceptDrop(Object info) {
if (info instanceof ItemInfo) {
ItemInfo item = (ItemInfo) info;
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
return true;
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return true;
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
item instanceof AppInfo) {
AppInfo appInfo = (AppInfo) info;
return (appInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
item instanceof ShortcutInfo) {
return true;
}
}
return false;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
boolean isVisible = true;
boolean useUninstallLabel = isAllAppsApplication(source, info);
boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
// If we are dragging an application from AppsCustomize, only show the control if we can
// delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
// Hide the delete target if it is a widget from AppsCustomize.
if (!willAcceptDrop(info) || isWidget(source, info)) {
isVisible = false;
}
if (useUninstallLabel) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
UserManager userManager = (UserManager)
getContext().getSystemService(Context.USER_SERVICE);
Bundle restrictions = userManager.getUserRestrictions();
if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
|| restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
isVisible = false;
}
}
}
if (useUninstallLabel) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
} else if (useDeleteLabel) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null);
} else {
isVisible = false;
}
mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
mActive = isVisible;
resetHoverColor();
((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
if (isVisible && getText().length() > 0) {
setText(useUninstallLabel ? R.string.delete_target_uninstall_label
: R.string.delete_target_label);
}
protected boolean supportsDrop(DragSource source, Object info) {
return willAcceptDrop(source, info);
}
@Override
public void onDragEnd() {
super.onDragEnd();
mActive = false;
}
public void onDragEnter(DragObject d) {
super.onDragEnter(d);
setHoverColor();
}
public void onDragExit(DragObject d) {
super.onDragExit(d);
if (!d.dragComplete) {
resetHoverColor();
} else {
// Restore the hover color if we are deleting
d.dragView.setColor(mHoverColor);
}
}
private void animateToTrashAndCompleteDrop(final DragObject d) {
final DragLayer dragLayer = mLauncher.getDragLayer();
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
width, height);
final float scale = (float) to.width() / from.width();
mSearchDropTargetBar.deferOnDragEnd();
deferCompleteDropIfUninstalling(d);
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
completeDrop(d);
mSearchDropTargetBar.onDragEnd();
mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2),
new LinearInterpolator(), onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
private void deferCompleteDropIfUninstalling(DragObject d) {
mWaitingForUninstall = false;
if (isUninstallFromWorkspace(d)) {
if (d.dragSource instanceof Folder) {
((Folder) d.dragSource).deferCompleteDropAfterUninstallActivity();
} else if (d.dragSource instanceof Workspace) {
((Workspace) d.dragSource).deferCompleteDropAfterUninstallActivity();
}
mWaitingForUninstall = true;
}
}
private boolean isUninstallFromWorkspace(DragObject d) {
return false;
}
@Thunk void completeDrop(DragObject d) {
ItemInfo item = (ItemInfo) d.dragInfo;
boolean wasWaitingForUninstall = mWaitingForUninstall;
mWaitingForUninstall = false;
if (isAllAppsApplication(d.dragSource, item)) {
uninstallApp(mLauncher, (AppInfo) item);
} else if (isUninstallFromWorkspace(d)) {
ShortcutInfo shortcut = (ShortcutInfo) item;
if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
final ComponentName componentName = shortcut.intent.getComponent();
final DragSource dragSource = d.dragSource;
final UserHandleCompat user = shortcut.user;
mWaitingForUninstall = mLauncher.startApplicationUninstallActivity(
componentName, shortcut.flags, user);
if (mWaitingForUninstall) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
mWaitingForUninstall = false;
String packageName = componentName.getPackageName();
boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
getContext(), packageName, user);
if (dragSource instanceof Folder) {
((Folder) dragSource).
onUninstallActivityReturned(uninstallSuccessful);
} else if (dragSource instanceof Workspace) {
((Workspace) dragSource).
onUninstallActivityReturned(uninstallSuccessful);
}
}
};
mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
}
}
} else if (isDragSourceWorkspaceOrFolder(d)) {
if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
removeWorkspaceOrFolderItem(mLauncher, item, null);
}
if (wasWaitingForUninstall && !mWaitingForUninstall) {
if (d.dragSource instanceof Folder) {
((Folder) d.dragSource).onUninstallActivityReturned(false);
} else if (d.dragSource instanceof Workspace) {
((Workspace) d.dragSource).onUninstallActivityReturned(false);
}
}
}
public static void uninstallApp(Launcher launcher, AppInfo info) {
launcher.startApplicationUninstallActivity(info.componentName, info.flags, info.user);
}
/**
@ -354,7 +105,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
appWidgetHost.deleteAppWidgetId(widget.appWidgetId);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
} else {
return false;
@ -367,18 +118,14 @@ public class DeleteDropTarget extends ButtonDropTarget {
return true;
}
public void onDrop(DragObject d) {
animateToTrashAndCompleteDrop(d);
}
/**
* Creates an animation from the current drag view to the delete trash icon.
*/
private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
DragObject d, PointF vel, ViewConfiguration config) {
int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
width, height);
final Rect from = new Rect();
@ -541,7 +288,6 @@ public class DeleteDropTarget extends ButtonDropTarget {
updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
duration, config);
}
deferCompleteDropIfUninstalling(d);
Runnable onAnimationEndRunnable = new Runnable() {
@Override

View File

@ -125,7 +125,7 @@ public class DragController {
/**
* Interface to receive notifications when a drag starts or stops
*/
interface DragListener {
public interface DragListener {
/**
* A drag has begun
*
@ -400,7 +400,7 @@ public class DragController {
}
}
void onDeferredEndFling(DropTarget.DragObject d) {
public void onDeferredEndFling(DropTarget.DragObject d) {
d.dragSource.onFlingToDeleteCompleted();
}
@ -462,7 +462,8 @@ public class DragController {
mLastTouchUpTime = System.currentTimeMillis();
if (mDragging) {
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) {
if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource,
mDragObject.dragInfo)) {
vec = null;
}
if (vec != null) {
@ -616,7 +617,7 @@ public class DragController {
if (mDragging) {
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) {
if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, mDragObject.dragInfo)) {
vec = null;
}
if (vec != null) {

View File

@ -139,7 +139,7 @@ public interface DropTarget {
/**
* Handle an object being dropped on the DropTarget
*
*
* @param source DragSource where the drag started
* @param x X coordinate of the drop location
* @param y Y coordinate of the drop location
@ -169,7 +169,7 @@ public interface DropTarget {
/**
* Check if a drop action can occur at, or near, the requested location.
* This will be called just before onDrop.
*
*
* @param source DragSource where the drag started
* @param x X coordinate of the drop location
* @param y Y coordinate of the drop location

View File

@ -51,6 +51,7 @@ import android.widget.TextView;
import com.android.launcher3.DragController.DragListener;
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.util.Thunk;
@ -62,7 +63,7 @@ import java.util.Collections;
*/
public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener {
View.OnFocusChangeListener, DragListener, UninstallSource {
private static final String TAG = "Launcher.Folder";
/**
@ -772,10 +773,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
updateItemLocationsInDatabaseBatch();
}
@Override
public void deferCompleteDropAfterUninstallActivity() {
mDeferDropAfterUninstall = true;
}
@Override
public void onUninstallActivityReturned(boolean success) {
mDeferDropAfterUninstall = false;
mUninstallSuccessful = success;

View File

@ -18,21 +18,14 @@ package com.android.launcher3;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.TransitionDrawable;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.R;
import com.android.launcher3.compat.UserHandleCompat;
public class InfoDropTarget extends ButtonDropTarget {
private ColorStateList mOriginalTextColor;
private TransitionDrawable mDrawable;
public InfoDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@ -44,43 +37,10 @@ public class InfoDropTarget extends ButtonDropTarget {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mOriginalTextColor = getTextColors();
// Get the hover color
Resources r = getResources();
mHoverColor = r.getColor(R.color.info_target_hover_tint);
mDrawable = (TransitionDrawable) getCurrentDrawable();
mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
if (mDrawable == null) {
// TODO: investigate why this is ever happening. Presently only on one known device.
mDrawable = (TransitionDrawable) r.getDrawable(R.drawable.info_target_selector);
setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
}
if (null != mDrawable) {
mDrawable.setCrossFadeEnabled(true);
}
// Remove the text in the Phone UI in landscape
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (!LauncherAppState.getInstance().isScreenLarge()) {
setText("");
}
}
}
@Override
public boolean acceptDrop(DragObject d) {
// acceptDrop is called just before onDrop. We do the work here, rather than
// in onDrop, because it allows us to reject the drop (by returning false)
// so that the object being dragged isn't removed from the drag source.
startDetailsActivityForInfo(d.dragInfo, mLauncher);
// There is no post-drop animation, so clean up the DragView now
d.deferDragViewCleanupPostAnimation = false;
return false;
setDrawable(R.drawable.info_target_selector);
}
public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
@ -105,39 +65,14 @@ public class InfoDropTarget extends ButtonDropTarget {
}
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
boolean isVisible = true;
// Hide this button unless we are dragging something from AllApps
if (!source.supportsAppInfoDropTarget()) {
isVisible = false;
}
mActive = isVisible;
mDrawable.resetTransition();
setTextColor(mOriginalTextColor);
((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
protected boolean supportsDrop(DragSource source, Object info) {
return source.supportsAppInfoDropTarget() &&
Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
}
@Override
public void onDragEnd() {
super.onDragEnd();
mActive = false;
}
public void onDragEnter(DragObject d) {
super.onDragEnter(d);
mDrawable.startTransition(mTransitionDuration);
setTextColor(mHoverColor);
}
public void onDragExit(DragObject d) {
super.onDragExit(d);
if (!d.dragComplete) {
mDrawable.resetTransition();
setTextColor(mOriginalTextColor);
}
void completeDrop(DragObject d) {
startDetailsActivityForInfo(d.dragInfo, mLauncher);
}
}

View File

@ -105,7 +105,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
return true;
} else if (action == UNINSTALL) {
DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item);
AppInfo info = (AppInfo) item;
mLauncher.startApplicationUninstallActivity(info.componentName, info.flags, info.user);
return true;
} else if (action == MOVE) {
beginAccessibleDrag(host, item);

View File

@ -1079,7 +1079,7 @@ public class LauncherModel extends BroadcastReceiver
* @param context
* @param item
*/
static void deleteItemFromDatabase(Context context, final ItemInfo item) {
public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
items.add(item);
deleteItemsFromDatabase(context, items);
@ -1185,7 +1185,7 @@ public class LauncherModel extends BroadcastReceiver
/**
* Remove the contents of the specified folder from the database
*/
static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
public static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
@ -3106,6 +3106,9 @@ public class LauncherModel extends BroadcastReceiver
si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON
& ~ShortcutInfo.FLAG_AUTOINTALL_ICON
& ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
if (appInfo != null) {
si.flags = appInfo.flags;
}
infoUpdated = true;
si.updateIcon(mIconCache);
@ -3414,6 +3417,9 @@ public class LauncherModel extends BroadcastReceiver
info.user = user;
info.contentDescription = mUserManager.getBadgedLabelForUser(
info.title.toString(), info.user);
if (lai != null) {
info.flags = AppInfo.initFlags(lai);
}
return info;
}

View File

@ -44,11 +44,14 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
private boolean mIsSearchBarHidden;
private View mQSBSearchBar;
private View mDropTargetBar;
private ButtonDropTarget mInfoDropTarget;
private ButtonDropTarget mDeleteDropTarget;
private int mBarHeight;
private boolean mDeferOnDragEnd = false;
// Drop targets
private ButtonDropTarget mInfoDropTarget;
private ButtonDropTarget mDeleteDropTarget;
private ButtonDropTarget mUninstallDropTarget;
private boolean mEnableDropDownDropTargets;
public SearchDropTargetBar(Context context, AttributeSet attrs) {
@ -61,13 +64,19 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
public void setup(Launcher launcher, DragController dragController) {
dragController.addDragListener(this);
dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
dragController.addDragListener(mInfoDropTarget);
dragController.addDragListener(mDeleteDropTarget);
dragController.addDragListener(mUninstallDropTarget);
dragController.addDropTarget(mInfoDropTarget);
dragController.addDropTarget(mDeleteDropTarget);
dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
dragController.addDropTarget(mUninstallDropTarget);
mInfoDropTarget.setLauncher(launcher);
mDeleteDropTarget.setLauncher(launcher);
mUninstallDropTarget.setLauncher(launcher);
}
public void setQsbSearchBar(View qsb) {
@ -116,9 +125,11 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
mDropTargetBar = findViewById(R.id.drag_target_bar);
mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text);
mInfoDropTarget.setSearchDropTargetBar(this);
mDeleteDropTarget.setSearchDropTargetBar(this);
mUninstallDropTarget.setSearchDropTargetBar(this);
mEnableDropDownDropTargets =
getResources().getBoolean(R.bool.config_useDropTargetDownTransition);

View File

@ -0,0 +1,122 @@
package com.android.launcher3;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import com.android.launcher3.R;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.Thunk;
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.delete_target_hover_tint);
setDrawable(R.drawable.uninstall_target_selector);
}
@Override
protected boolean supportsDrop(DragSource source, Object info) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
UserManager userManager = (UserManager)
getContext().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;
}
}
Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
return componentInfo != null && (componentInfo.second & AppInfo.DOWNLOADED_FLAG) != 0;
}
/**
* @return the component name and flags if {@param info} is an AppInfo or an app shortcut.
*/
private static Pair<ComponentName, Integer> getAppInfoFlags(Object item) {
if (item instanceof AppInfo) {
AppInfo info = (AppInfo) item;
return Pair.create(info.componentName, info.flags);
} else if (item instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) item;
ComponentName component = info.getTargetComponent();
if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION
&& component != null) {
return Pair.create(component, info.flags);
}
}
return null;
}
@Override
public void onDrop(DragObject d) {
// Differ item deletion
if (d.dragSource instanceof UninstallSource) {
((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
}
super.onDrop(d);
}
@Override
void completeDrop(final DragObject d) {
final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo);
final UserHandleCompat user = ((ItemInfo) d.dragInfo).user;
if (mLauncher.startApplicationUninstallActivity(
componentInfo.first, componentInfo.second, user)) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
String packageName = componentInfo.first.getPackageName();
boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
getContext(), packageName, user);
sendUninstallResult(d.dragSource, uninstallSuccessful);
}
};
mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
} else {
sendUninstallResult(d.dragSource, false);
}
}
@Thunk void sendUninstallResult(DragSource target, boolean result) {
if (target instanceof UninstallSource) {
((UninstallSource) target).onUninstallActivityReturned(result);
}
}
/**
* Interface defining an object that can provide uninstallable drag objects.
*/
public static interface UninstallSource {
/**
* 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
* after some time.
*/
void deferCompleteDropAfterUninstallActivity();
}
}

View File

@ -66,6 +66,7 @@ import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
@ -88,7 +89,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class Workspace extends SmoothPagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
Insettable {
Insettable, UninstallSource {
private static final String TAG = "Launcher.Workspace";
private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
@ -4269,11 +4270,13 @@ public class Workspace extends SmoothPagedView
}
}
@Override
public void deferCompleteDropAfterUninstallActivity() {
mDeferDropAfterUninstall = true;
}
/// maybe move this into a smaller part
@Override
public void onUninstallActivityReturned(boolean success) {
mDeferDropAfterUninstall = false;
mUninstallSuccessful = success;