Loading src/com/android/launcher3/ButtonDropTarget.java +22 −13 Original line number Diff line number Diff line Loading @@ -18,8 +18,6 @@ package com.android.launcher3; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.launcher3.LauncherState.NORMAL; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; Loading @@ -34,12 +32,15 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.views.ActivityContext; /** * Implements a DropTarget. Loading @@ -59,8 +60,8 @@ public abstract class ButtonDropTarget extends TextView private final Rect mTempRect = new Rect(); protected final Launcher mLauncher; protected final ActivityContext mActivityContext; protected final DropTargetHandler mDropTargetHandler; protected DropTargetBar mDropTargetBar; /** Whether this drop target is active for the current drag */ Loading @@ -83,13 +84,17 @@ public abstract class ButtonDropTarget extends TextView private PopupWindow mToolTip; private int mToolTipLocation; public ButtonDropTarget(Context context) { this(context, null, 0); } public ButtonDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mLauncher = Launcher.getLauncher(context); mActivityContext = ActivityContext.lookupContext(context); mDropTargetHandler = mActivityContext.getDropTargetHandler(); Resources resources = getResources(); mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold); Loading Loading @@ -210,7 +215,8 @@ public abstract class ButtonDropTarget extends TextView @Override public boolean isDropEnabled() { return mActive && (mAccessibleDrag || mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold); mActivityContext.getDragController().getDistanceDragged() >= mDragDistanceThreshold); } @Override Loading @@ -229,7 +235,8 @@ public abstract class ButtonDropTarget extends TextView // FlingAnimation handles the animation and then calls completeDrop(). return; } final DragLayer dragLayer = mLauncher.getDragLayer(); final DragLayer dragLayer = mDropTargetHandler.getDragLayer(); final DragView dragView = d.dragView; final Rect to = getIconRect(d); final float scale = (float) to.width() / dragView.getMeasuredWidth(); Loading @@ -240,9 +247,10 @@ public abstract class ButtonDropTarget extends TextView Runnable onAnimationEndRunnable = () -> { completeDrop(d); mDropTargetBar.onDragEnd(); mLauncher.getStateManager().goToState(NORMAL); mDropTargetHandler.onDropAnimationComplete(); }; dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f, DRAG_VIEW_DROP_DURATION, Interpolators.DEACCEL_2, onAnimationEndRunnable, Loading @@ -261,10 +269,10 @@ public abstract class ButtonDropTarget extends TextView @Override public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) { super.getHitRect(outRect); outRect.bottom += mLauncher.getDeviceProfile().dropTargetDragPaddingPx; outRect.bottom += mActivityContext.getDeviceProfile().dropTargetDragPaddingPx; sTempCords[0] = sTempCords[1] = 0; mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords); mActivityContext.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords); outRect.offsetTo(sTempCords[0], sTempCords[1]); } Loading @@ -273,7 +281,7 @@ public abstract class ButtonDropTarget extends TextView int viewHeight = dragObject.dragView.getMeasuredHeight(); int drawableWidth = mDrawable.getIntrinsicWidth(); int drawableHeight = mDrawable.getIntrinsicHeight(); DragLayer dragLayer = mLauncher.getDragLayer(); DragLayer dragLayer = mDropTargetHandler.getDragLayer(); // Find the rect to animate to (the view is center aligned) Rect to = new Rect(); Loading Loading @@ -314,7 +322,7 @@ public abstract class ButtonDropTarget extends TextView @Override public void onClick(View v) { mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null); mDropTargetHandler.onClick(this); } public void setTextVisible(boolean isVisible) { Loading Loading @@ -407,7 +415,8 @@ public abstract class ButtonDropTarget extends TextView /** * Returns if the text will be clipped vertically within the provided availableHeight. */ private boolean isTextClippedVertically(int availableHeight) { @VisibleForTesting protected boolean isTextClippedVertically(int availableHeight) { availableHeight -= getPaddingTop() + getPaddingBottom(); if (availableHeight <= 0) { return true; Loading src/com/android/launcher3/DeleteDropTarget.java +8 −29 Original line number Diff line number Diff line Loading @@ -18,24 +18,19 @@ package com.android.launcher3; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO; import android.content.Context; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.IntSet; import com.android.launcher3.views.Snackbar; public class DeleteDropTarget extends ButtonDropTarget { Loading @@ -43,6 +38,10 @@ public class DeleteDropTarget extends ButtonDropTarget { private StatsLogManager.LauncherEvent mLauncherEvent; public DeleteDropTarget(Context context) { this(context, null, 0); } public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } Loading Loading @@ -120,7 +119,7 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override public void onDrop(DragObject d, DragOptions options) { if (canRemove(d.dragInfo)) { mLauncher.getModelWriter().prepareToUndoDelete(); mDropTargetHandler.prepareToUndoDelete(); } super.onDrop(d, options); mStatsLogManager.logger().withInstanceId(d.logInstanceId) Loading @@ -131,26 +130,8 @@ public class DeleteDropTarget extends ButtonDropTarget { public void completeDrop(DragObject d) { ItemInfo item = d.dragInfo; if (canRemove(item)) { ItemInfo pageItem = item; if (item.container <= 0) { View v = mLauncher.getWorkspace().getHomescreenIconByItemId(item.container); if (v != null) { pageItem = (ItemInfo) v.getTag(); } } IntSet pageIds = pageItem.container == Favorites.CONTAINER_DESKTOP ? IntSet.wrap(pageItem.screenId) : mLauncher.getWorkspace().getCurrentPageScreenIds(); onAccessibilityDrop(null, item); ModelWriter modelWriter = mLauncher.getModelWriter(); Runnable onUndoClicked = () -> { mLauncher.setPagesToBindSynchronously(pageIds); modelWriter.abortDelete(); mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO); }; Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, modelWriter::commitDelete, onUndoClicked); mDropTargetHandler.onDeleteComplete(item); } } Loading @@ -162,9 +143,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop"); mLauncher.getWorkspace().stripEmptyScreens(); mLauncher.getDragLayer() .announceForAccessibility(getContext().getString(R.string.item_removed)); CharSequence announcement = getContext().getString(R.string.item_removed); mDropTargetHandler.onAccessibilityDelete(view, item, announcement); } } src/com/android/launcher3/DropTargetHandler.kt 0 → 100644 +119 −0 Original line number Diff line number Diff line package com.android.launcher3 import android.content.ComponentName import android.view.View import com.android.launcher3.DropTarget.DragObject import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete import com.android.launcher3.dragndrop.DragLayer import com.android.launcher3.logging.StatsLogManager.LauncherEvent import com.android.launcher3.model.ModelWriter import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.util.IntSet import com.android.launcher3.util.PendingRequestArgs import com.android.launcher3.views.Snackbar /** * Handler class for drop target actions that require modifying or interacting with launcher. * * This class is created by Launcher and provided the instance of launcher when created, which * allows us to decouple drop target controllers from Launcher to enable easier testing. */ class DropTargetHandler(launcher: Launcher) { val mLauncher: Launcher = launcher val modelWriter: ModelWriter = mLauncher.modelWriter fun onDropAnimationComplete() { mLauncher.stateManager.goToState(LauncherState.NORMAL) } fun onSecondaryTargetCompleteDrop(target: ComponentName?, d: DragObject) { when (val dragSource = d.dragSource) { is DeferredOnComplete -> { val deferred: DeferredOnComplete = dragSource if (d.dragSource is SecondaryDropTarget.DeferredOnComplete) { target?.let { deferred.mPackageName = it.packageName mLauncher.addOnResumeCallback { deferred.onLauncherResume() } } ?: deferred.sendFailure() } } } } fun reconfigureWidget(widgetId: Int, info: ItemInfo) { mLauncher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(widgetId, null, info)) mLauncher.appWidgetHolder.startConfigActivity( mLauncher, widgetId, Launcher.REQUEST_RECONFIGURE_APPWIDGET ) } fun dismissPrediction( announcement: CharSequence, onActionClicked: Runnable, onDismiss: Runnable? ) { mLauncher.dragLayer.announceForAccessibility(announcement) Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, onDismiss, onActionClicked) } fun getViewUnderDrag(info: ItemInfo): View? { return if ( info is LauncherAppWidgetInfo && info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && mLauncher.workspace.dragInfo != null ) { mLauncher.workspace.dragInfo.cell } else null } fun prepareToUndoDelete() { mLauncher.modelWriter.prepareToUndoDelete() } fun onDeleteComplete(item: ItemInfo) { var pageItem: ItemInfo = item if (item.container <= 0) { val v = mLauncher.workspace.getHomescreenIconByItemId(item.container) v?.let { pageItem = v.tag as ItemInfo } } val pageIds = if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) IntSet.wrap(pageItem.screenId) else mLauncher.workspace.currentPageScreenIds val onUndoClicked = Runnable { mLauncher.setPagesToBindSynchronously(pageIds) modelWriter.abortDelete() mLauncher.statsLogManager.logger().log(LauncherEvent.LAUNCHER_UNDO) } Snackbar.show( mLauncher, R.string.item_removed, R.string.undo, modelWriter::commitDelete, onUndoClicked ) } fun onAccessibilityDelete(view: View?, item: ItemInfo, announcement: CharSequence) { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop") mLauncher.workspace.stripEmptyScreens() mLauncher.dragLayer.announceForAccessibility(announcement) } fun getDragLayer(): DragLayer { return mLauncher.dragLayer } fun onClick(buttonDropTarget: ButtonDropTarget) { mLauncher.accessibilityDelegate.handleAccessibleDrop(buttonDropTarget, null, null) } } src/com/android/launcher3/Launcher.java +5 −0 Original line number Diff line number Diff line Loading @@ -1770,6 +1770,11 @@ public class Launcher extends StatefulActivity<LauncherState> return mDragController; } @Override public DropTargetHandler getDropTargetHandler() { return new DropTargetHandler(this); } @Override public void startActivityForResult(Intent intent, int requestCode, Bundle options) { if (requestCode != -1) { Loading src/com/android/launcher3/SecondaryDropTarget.java +16 −33 Original line number Diff line number Diff line Loading @@ -3,8 +3,6 @@ package com.android.launcher3; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE; import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.INVALID; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE; Loading Loading @@ -45,10 +43,7 @@ import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.views.Snackbar; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import java.net.URISyntaxException; Loading Loading @@ -204,7 +199,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList user = item.user; } if (intent != null) { LauncherActivityInfo info = mLauncher.getSystemService(LauncherApps.class) LauncherActivityInfo info = getContext().getSystemService(LauncherApps.class) .resolveActivity(intent, user); if (info != null && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) { Loading Loading @@ -239,23 +234,11 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList public void completeDrop(final DragObject d) { ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo, d.logInstanceId); if (d.dragSource instanceof DeferredOnComplete) { DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource; if (target != null) { deferred.mPackageName = target.getPackageName(); mLauncher.addOnResumeCallback(deferred::onLauncherResume); } else { deferred.sendFailure(); } } mDropTargetHandler.onSecondaryTargetCompleteDrop(target, d); } private View getViewUnderDrag(ItemInfo info) { if (info instanceof LauncherAppWidgetInfo && info.container == CONTAINER_DESKTOP && mLauncher.getWorkspace().getDragInfo() != null) { return mLauncher.getWorkspace().getDragInfo().cell; } return null; return mDropTargetHandler.getViewUnderDrag(info); } /** Loading Loading @@ -286,18 +269,15 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList if (mCurrentAccessibilityAction == RECONFIGURE) { int widgetId = getReconfigurableWidgetId(view); if (widgetId != INVALID_APPWIDGET_ID) { mLauncher.setWaitingForResult( PendingRequestArgs.forWidgetInfo(widgetId, null, info)); mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId, REQUEST_RECONFIGURE_APPWIDGET); mDropTargetHandler.reconfigureWidget(widgetId, info); } return null; } if (mCurrentAccessibilityAction == DISMISS_PREDICTION) { if (FeatureFlags.ENABLE_DISMISS_PREDICTION_UNDO.get()) { mLauncher.getDragLayer() .announceForAccessibility(getContext().getString(R.string.item_removed)); Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, () -> { }, () -> { CharSequence announcement = getContext().getString(R.string.item_removed); mDropTargetHandler .dismissPrediction(announcement, () -> {}, () -> { mStatsLogManager.logger() .withInstanceId(instanceId) .withItemInfo(info) Loading @@ -306,20 +286,23 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList } return null; } // else: mCurrentAccessibilityAction == UNINSTALL ComponentName cn = getUninstallTarget(info); 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(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); Toast.makeText( getContext(), R.string.uninstall_system_app_text, Toast.LENGTH_SHORT ).show(); return null; } try { Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0) Intent i = Intent.parseUri(getContext().getString(R.string.delete_package_intent), 0) .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) .putExtra(Intent.EXTRA_USER, info.user); mLauncher.startActivity(i); getContext().startActivity(i); FileLog.d(TAG, "start uninstall activity " + cn.getPackageName()); return cn; } catch (URISyntaxException e) { Loading @@ -339,12 +322,12 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until * {@link #onLauncherResume} */ private class DeferredOnComplete implements DragSource { protected class DeferredOnComplete implements DragSource { private final DragSource mOriginal; private final Context mContext; private String mPackageName; protected String mPackageName; private DragObject mDragObject; public DeferredOnComplete(DragSource original, Context context) { Loading Loading
src/com/android/launcher3/ButtonDropTarget.java +22 −13 Original line number Diff line number Diff line Loading @@ -18,8 +18,6 @@ package com.android.launcher3; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.launcher3.LauncherState.NORMAL; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; Loading @@ -34,12 +32,15 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.views.ActivityContext; /** * Implements a DropTarget. Loading @@ -59,8 +60,8 @@ public abstract class ButtonDropTarget extends TextView private final Rect mTempRect = new Rect(); protected final Launcher mLauncher; protected final ActivityContext mActivityContext; protected final DropTargetHandler mDropTargetHandler; protected DropTargetBar mDropTargetBar; /** Whether this drop target is active for the current drag */ Loading @@ -83,13 +84,17 @@ public abstract class ButtonDropTarget extends TextView private PopupWindow mToolTip; private int mToolTipLocation; public ButtonDropTarget(Context context) { this(context, null, 0); } public ButtonDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mLauncher = Launcher.getLauncher(context); mActivityContext = ActivityContext.lookupContext(context); mDropTargetHandler = mActivityContext.getDropTargetHandler(); Resources resources = getResources(); mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold); Loading Loading @@ -210,7 +215,8 @@ public abstract class ButtonDropTarget extends TextView @Override public boolean isDropEnabled() { return mActive && (mAccessibleDrag || mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold); mActivityContext.getDragController().getDistanceDragged() >= mDragDistanceThreshold); } @Override Loading @@ -229,7 +235,8 @@ public abstract class ButtonDropTarget extends TextView // FlingAnimation handles the animation and then calls completeDrop(). return; } final DragLayer dragLayer = mLauncher.getDragLayer(); final DragLayer dragLayer = mDropTargetHandler.getDragLayer(); final DragView dragView = d.dragView; final Rect to = getIconRect(d); final float scale = (float) to.width() / dragView.getMeasuredWidth(); Loading @@ -240,9 +247,10 @@ public abstract class ButtonDropTarget extends TextView Runnable onAnimationEndRunnable = () -> { completeDrop(d); mDropTargetBar.onDragEnd(); mLauncher.getStateManager().goToState(NORMAL); mDropTargetHandler.onDropAnimationComplete(); }; dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f, DRAG_VIEW_DROP_DURATION, Interpolators.DEACCEL_2, onAnimationEndRunnable, Loading @@ -261,10 +269,10 @@ public abstract class ButtonDropTarget extends TextView @Override public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) { super.getHitRect(outRect); outRect.bottom += mLauncher.getDeviceProfile().dropTargetDragPaddingPx; outRect.bottom += mActivityContext.getDeviceProfile().dropTargetDragPaddingPx; sTempCords[0] = sTempCords[1] = 0; mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords); mActivityContext.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords); outRect.offsetTo(sTempCords[0], sTempCords[1]); } Loading @@ -273,7 +281,7 @@ public abstract class ButtonDropTarget extends TextView int viewHeight = dragObject.dragView.getMeasuredHeight(); int drawableWidth = mDrawable.getIntrinsicWidth(); int drawableHeight = mDrawable.getIntrinsicHeight(); DragLayer dragLayer = mLauncher.getDragLayer(); DragLayer dragLayer = mDropTargetHandler.getDragLayer(); // Find the rect to animate to (the view is center aligned) Rect to = new Rect(); Loading Loading @@ -314,7 +322,7 @@ public abstract class ButtonDropTarget extends TextView @Override public void onClick(View v) { mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null); mDropTargetHandler.onClick(this); } public void setTextVisible(boolean isVisible) { Loading Loading @@ -407,7 +415,8 @@ public abstract class ButtonDropTarget extends TextView /** * Returns if the text will be clipped vertically within the provided availableHeight. */ private boolean isTextClippedVertically(int availableHeight) { @VisibleForTesting protected boolean isTextClippedVertically(int availableHeight) { availableHeight -= getPaddingTop() + getPaddingBottom(); if (availableHeight <= 0) { return true; Loading
src/com/android/launcher3/DeleteDropTarget.java +8 −29 Original line number Diff line number Diff line Loading @@ -18,24 +18,19 @@ package com.android.launcher3; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO; import android.content.Context; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.IntSet; import com.android.launcher3.views.Snackbar; public class DeleteDropTarget extends ButtonDropTarget { Loading @@ -43,6 +38,10 @@ public class DeleteDropTarget extends ButtonDropTarget { private StatsLogManager.LauncherEvent mLauncherEvent; public DeleteDropTarget(Context context) { this(context, null, 0); } public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } Loading Loading @@ -120,7 +119,7 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override public void onDrop(DragObject d, DragOptions options) { if (canRemove(d.dragInfo)) { mLauncher.getModelWriter().prepareToUndoDelete(); mDropTargetHandler.prepareToUndoDelete(); } super.onDrop(d, options); mStatsLogManager.logger().withInstanceId(d.logInstanceId) Loading @@ -131,26 +130,8 @@ public class DeleteDropTarget extends ButtonDropTarget { public void completeDrop(DragObject d) { ItemInfo item = d.dragInfo; if (canRemove(item)) { ItemInfo pageItem = item; if (item.container <= 0) { View v = mLauncher.getWorkspace().getHomescreenIconByItemId(item.container); if (v != null) { pageItem = (ItemInfo) v.getTag(); } } IntSet pageIds = pageItem.container == Favorites.CONTAINER_DESKTOP ? IntSet.wrap(pageItem.screenId) : mLauncher.getWorkspace().getCurrentPageScreenIds(); onAccessibilityDrop(null, item); ModelWriter modelWriter = mLauncher.getModelWriter(); Runnable onUndoClicked = () -> { mLauncher.setPagesToBindSynchronously(pageIds); modelWriter.abortDelete(); mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO); }; Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, modelWriter::commitDelete, onUndoClicked); mDropTargetHandler.onDeleteComplete(item); } } Loading @@ -162,9 +143,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop"); mLauncher.getWorkspace().stripEmptyScreens(); mLauncher.getDragLayer() .announceForAccessibility(getContext().getString(R.string.item_removed)); CharSequence announcement = getContext().getString(R.string.item_removed); mDropTargetHandler.onAccessibilityDelete(view, item, announcement); } }
src/com/android/launcher3/DropTargetHandler.kt 0 → 100644 +119 −0 Original line number Diff line number Diff line package com.android.launcher3 import android.content.ComponentName import android.view.View import com.android.launcher3.DropTarget.DragObject import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete import com.android.launcher3.dragndrop.DragLayer import com.android.launcher3.logging.StatsLogManager.LauncherEvent import com.android.launcher3.model.ModelWriter import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.util.IntSet import com.android.launcher3.util.PendingRequestArgs import com.android.launcher3.views.Snackbar /** * Handler class for drop target actions that require modifying or interacting with launcher. * * This class is created by Launcher and provided the instance of launcher when created, which * allows us to decouple drop target controllers from Launcher to enable easier testing. */ class DropTargetHandler(launcher: Launcher) { val mLauncher: Launcher = launcher val modelWriter: ModelWriter = mLauncher.modelWriter fun onDropAnimationComplete() { mLauncher.stateManager.goToState(LauncherState.NORMAL) } fun onSecondaryTargetCompleteDrop(target: ComponentName?, d: DragObject) { when (val dragSource = d.dragSource) { is DeferredOnComplete -> { val deferred: DeferredOnComplete = dragSource if (d.dragSource is SecondaryDropTarget.DeferredOnComplete) { target?.let { deferred.mPackageName = it.packageName mLauncher.addOnResumeCallback { deferred.onLauncherResume() } } ?: deferred.sendFailure() } } } } fun reconfigureWidget(widgetId: Int, info: ItemInfo) { mLauncher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(widgetId, null, info)) mLauncher.appWidgetHolder.startConfigActivity( mLauncher, widgetId, Launcher.REQUEST_RECONFIGURE_APPWIDGET ) } fun dismissPrediction( announcement: CharSequence, onActionClicked: Runnable, onDismiss: Runnable? ) { mLauncher.dragLayer.announceForAccessibility(announcement) Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, onDismiss, onActionClicked) } fun getViewUnderDrag(info: ItemInfo): View? { return if ( info is LauncherAppWidgetInfo && info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && mLauncher.workspace.dragInfo != null ) { mLauncher.workspace.dragInfo.cell } else null } fun prepareToUndoDelete() { mLauncher.modelWriter.prepareToUndoDelete() } fun onDeleteComplete(item: ItemInfo) { var pageItem: ItemInfo = item if (item.container <= 0) { val v = mLauncher.workspace.getHomescreenIconByItemId(item.container) v?.let { pageItem = v.tag as ItemInfo } } val pageIds = if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) IntSet.wrap(pageItem.screenId) else mLauncher.workspace.currentPageScreenIds val onUndoClicked = Runnable { mLauncher.setPagesToBindSynchronously(pageIds) modelWriter.abortDelete() mLauncher.statsLogManager.logger().log(LauncherEvent.LAUNCHER_UNDO) } Snackbar.show( mLauncher, R.string.item_removed, R.string.undo, modelWriter::commitDelete, onUndoClicked ) } fun onAccessibilityDelete(view: View?, item: ItemInfo, announcement: CharSequence) { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop") mLauncher.workspace.stripEmptyScreens() mLauncher.dragLayer.announceForAccessibility(announcement) } fun getDragLayer(): DragLayer { return mLauncher.dragLayer } fun onClick(buttonDropTarget: ButtonDropTarget) { mLauncher.accessibilityDelegate.handleAccessibleDrop(buttonDropTarget, null, null) } }
src/com/android/launcher3/Launcher.java +5 −0 Original line number Diff line number Diff line Loading @@ -1770,6 +1770,11 @@ public class Launcher extends StatefulActivity<LauncherState> return mDragController; } @Override public DropTargetHandler getDropTargetHandler() { return new DropTargetHandler(this); } @Override public void startActivityForResult(Intent intent, int requestCode, Bundle options) { if (requestCode != -1) { Loading
src/com/android/launcher3/SecondaryDropTarget.java +16 −33 Original line number Diff line number Diff line Loading @@ -3,8 +3,6 @@ package com.android.launcher3; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE; import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.INVALID; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE; Loading Loading @@ -45,10 +43,7 @@ import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.views.Snackbar; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import java.net.URISyntaxException; Loading Loading @@ -204,7 +199,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList user = item.user; } if (intent != null) { LauncherActivityInfo info = mLauncher.getSystemService(LauncherApps.class) LauncherActivityInfo info = getContext().getSystemService(LauncherApps.class) .resolveActivity(intent, user); if (info != null && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) { Loading Loading @@ -239,23 +234,11 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList public void completeDrop(final DragObject d) { ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo, d.logInstanceId); if (d.dragSource instanceof DeferredOnComplete) { DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource; if (target != null) { deferred.mPackageName = target.getPackageName(); mLauncher.addOnResumeCallback(deferred::onLauncherResume); } else { deferred.sendFailure(); } } mDropTargetHandler.onSecondaryTargetCompleteDrop(target, d); } private View getViewUnderDrag(ItemInfo info) { if (info instanceof LauncherAppWidgetInfo && info.container == CONTAINER_DESKTOP && mLauncher.getWorkspace().getDragInfo() != null) { return mLauncher.getWorkspace().getDragInfo().cell; } return null; return mDropTargetHandler.getViewUnderDrag(info); } /** Loading Loading @@ -286,18 +269,15 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList if (mCurrentAccessibilityAction == RECONFIGURE) { int widgetId = getReconfigurableWidgetId(view); if (widgetId != INVALID_APPWIDGET_ID) { mLauncher.setWaitingForResult( PendingRequestArgs.forWidgetInfo(widgetId, null, info)); mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId, REQUEST_RECONFIGURE_APPWIDGET); mDropTargetHandler.reconfigureWidget(widgetId, info); } return null; } if (mCurrentAccessibilityAction == DISMISS_PREDICTION) { if (FeatureFlags.ENABLE_DISMISS_PREDICTION_UNDO.get()) { mLauncher.getDragLayer() .announceForAccessibility(getContext().getString(R.string.item_removed)); Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, () -> { }, () -> { CharSequence announcement = getContext().getString(R.string.item_removed); mDropTargetHandler .dismissPrediction(announcement, () -> {}, () -> { mStatsLogManager.logger() .withInstanceId(instanceId) .withItemInfo(info) Loading @@ -306,20 +286,23 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList } return null; } // else: mCurrentAccessibilityAction == UNINSTALL ComponentName cn = getUninstallTarget(info); 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(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); Toast.makeText( getContext(), R.string.uninstall_system_app_text, Toast.LENGTH_SHORT ).show(); return null; } try { Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0) Intent i = Intent.parseUri(getContext().getString(R.string.delete_package_intent), 0) .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) .putExtra(Intent.EXTRA_USER, info.user); mLauncher.startActivity(i); getContext().startActivity(i); FileLog.d(TAG, "start uninstall activity " + cn.getPackageName()); return cn; } catch (URISyntaxException e) { Loading @@ -339,12 +322,12 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until * {@link #onLauncherResume} */ private class DeferredOnComplete implements DragSource { protected class DeferredOnComplete implements DragSource { private final DragSource mOriginal; private final Context mContext; private String mPackageName; protected String mPackageName; private DragObject mDragObject; public DeferredOnComplete(DragSource original, Context context) { Loading