diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 2b0da434a9..34d70673ae 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -155,7 +155,7 @@ public abstract class ButtonDropTarget extends TextView @Override public final void onDragEnter(DragObject d) { - if (!d.accessibleDrag && !mTextVisible) { + if (!mAccessibleDrag && !mTextVisible) { // Show tooltip hideTooltip(); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e3eb38792a..4742bbc766 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -53,8 +53,6 @@ import androidx.core.view.ViewCompat; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; -import com.android.launcher3.accessibility.FolderAccessibilityHelper; -import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.config.FeatureFlags; @@ -79,9 +77,6 @@ import java.util.Comparator; import java.util.Stack; public class CellLayout extends ViewGroup implements Transposable { - public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2; - public static final int FOLDER_ACCESSIBILITY_DRAG = 1; - private static final String TAG = "CellLayout"; private static final boolean LOGD = false; @@ -182,7 +177,6 @@ public class CellLayout extends ViewGroup implements Transposable { private static final Paint sPaint = new Paint(); // Related to accessible drag and drop - private DragAndDropAccessibilityDelegate mTouchHelper; private boolean mUseTouchHelper = false; private RotationMode mRotationMode = RotationMode.NORMAL; @@ -292,26 +286,20 @@ public class CellLayout extends ViewGroup implements Transposable { addView(mShortcutsAndWidgets); } - public void enableAccessibleDrag(boolean enable, int dragType) { - mUseTouchHelper = enable; - if (!enable) { - ViewCompat.setAccessibilityDelegate(this, null); - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - setOnClickListener(null); - } else { - if (dragType == WORKSPACE_ACCESSIBILITY_DRAG && - !(mTouchHelper instanceof WorkspaceAccessibilityHelper)) { - mTouchHelper = new WorkspaceAccessibilityHelper(this); - } else if (dragType == FOLDER_ACCESSIBILITY_DRAG && - !(mTouchHelper instanceof FolderAccessibilityHelper)) { - mTouchHelper = new FolderAccessibilityHelper(this); - } - ViewCompat.setAccessibilityDelegate(this, mTouchHelper); - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - setOnClickListener(mTouchHelper); - } + + /** + * Sets or clears a delegate used for accessible drag and drop + */ + public void setDragAndDropAccessibilityDelegate(DragAndDropAccessibilityDelegate delegate) { + setOnClickListener(delegate); + setOnHoverListener(delegate); + ViewCompat.setAccessibilityDelegate(this, delegate); + + mUseTouchHelper = delegate != null; + int accessibilityFlag = mUseTouchHelper + ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO; + setImportantForAccessibility(accessibilityFlag); + getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag); // Invalidate the accessibility hierarchy if (getParent() != null) { @@ -338,15 +326,6 @@ public class CellLayout extends ViewGroup implements Transposable { super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); } - @Override - public boolean dispatchHoverEvent(MotionEvent event) { - // Always attempt to dispatch hover events to accessibility first. - if (mUseTouchHelper && mTouchHelper.dispatchHoverEvent(event)) { - return true; - } - return super.dispatchHoverEvent(event); - } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mUseTouchHelper || diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index a32fd1239f..c03011b7b2 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -59,9 +59,6 @@ public interface DropTarget { /** Where the drag originated */ public DragSource dragSource = null; - /** The object is part of an accessible drag operation */ - public boolean accessibleDrag; - /** Indicates that the drag operation was cancelled */ public boolean cancelled = false; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1413a5cd1f..1dd2e41567 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -339,6 +339,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } }; + private long mLastTouchUpTime = -1; + @Override protected void onCreate(Bundle savedInstanceState) { Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT, @@ -1115,7 +1117,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, super.onPause(); mDragController.cancelDrag(); - mDragController.resetLastGestureUpTime(); + mLastTouchUpTime = -1; mDropTargetBar.animateToVisibility(false); if (!mDeferOverlayCallbacks) { @@ -1838,6 +1840,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP) { + mLastTouchUpTime = System.currentTimeMillis(); + } TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); return super.dispatchTouchEvent(ev); } @@ -2465,8 +2470,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } private boolean canRunNewAppsAnimation() { - long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime(); - return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); + if (mDragController.isDragging()) { + return false; + } else { + return (System.currentTimeMillis() - mLastTouchUpTime) + > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); + } } private ValueAnimator createNewAppBounceAnimation(View v, int i) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index ead6018505..46493b7959 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -192,10 +192,7 @@ public class Workspace extends PagedView final WallpaperOffsetInterpolator mWallpaperOffset; private boolean mUnlockWallpaperFromDefaultPageOnLayout; - // Variables relating to the creation of user folders by hovering shortcuts over shortcuts - private static final int FOLDER_CREATION_TIMEOUT = 0; public static final int REORDER_TIMEOUT = 650; - private final Alarm mFolderCreationAlarm = new Alarm(); private final Alarm mReorderAlarm = new Alarm(); private PreviewBackground mFolderCreateBg; private FolderIcon mDragOverFolderIcon = null; @@ -567,11 +564,6 @@ public class Workspace extends PagedView addView(newScreen, insertIndex); mStateTransitionAnimation.applyChildState( mLauncher.getStateManager().getState(), newScreen, insertIndex); - - if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { - newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); - } - return newScreen; } @@ -818,11 +810,6 @@ public class Workspace extends PagedView if (indexOfChild(cl) < currentPage) { pageShift++; } - - if (isInAccessibleDrag) { - cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); - } - removeView(cl); } else { // if this is the last screen, convert it to the empty screen @@ -1444,14 +1431,14 @@ public class Workspace extends PagedView child.setVisibility(INVISIBLE); if (options.isAccessibleDrag) { - mDragController.addDragListener(new AccessibleDragListenerAdapter( - this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) { - @Override - protected void enableAccessibleDrag(boolean enable) { - super.enableAccessibleDrag(enable); - setEnableForLayout(mLauncher.getHotseat(),enable); - } - }); + mDragController.addDragListener( + new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) { + @Override + protected void enableAccessibleDrag(boolean enable) { + super.enableAccessibleDrag(enable); + setEnableForLayout(mLauncher.getHotseat(), enable); + } + }); } beginDragShared(child, this, options); @@ -1883,12 +1870,10 @@ public class Workspace extends PagedView final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE - && !d.accessibleDrag) { - onCompleteRunnable = new Runnable() { - public void run() { - if (!isPageInTransition()) { - AppWidgetResizeFrame.showForWidget(hostView, cellLayout); - } + && !options.isAccessibleDrag) { + onCompleteRunnable = () -> { + if (!isPageInTransition()) { + AppWidgetResizeFrame.showForWidget(hostView, cellLayout); } }; } @@ -2088,8 +2073,6 @@ public class Workspace extends PagedView if (mFolderCreateBg != null) { mFolderCreateBg.animateToRest(); } - mFolderCreationAlarm.setOnAlarmListener(null); - mFolderCreationAlarm.cancelAlarm(); } private void cleanupAddToFolder() { @@ -2196,7 +2179,7 @@ public class Workspace extends PagedView float targetCellDistance = mDragTargetLayout.getDistanceFromCell( mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); - manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d); + manageFolderFeedback(targetCellDistance, d); boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, @@ -2294,8 +2277,7 @@ public class Workspace extends PagedView return null; } - private void manageFolderFeedback(CellLayout targetLayout, - int[] targetCell, float distance, DragObject dragObject) { + private void manageFolderFeedback(float distance, DragObject dragObject) { if (distance > mMaxDistanceForFolderCreation) { if (mDragMode != DRAG_MODE_NONE) { setDragMode(DRAG_MODE_NONE); @@ -2306,18 +2288,18 @@ public class Workspace extends PagedView final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]); ItemInfo info = dragObject.dragInfo; boolean userFolderPending = willCreateUserFolder(info, dragOverView, false); - if (mDragMode == DRAG_MODE_NONE && userFolderPending && - !mFolderCreationAlarm.alarmPending()) { + if (mDragMode == DRAG_MODE_NONE && userFolderPending) { - FolderCreationAlarmListener listener = new - FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]); + mFolderCreateBg = new PreviewBackground(); + mFolderCreateBg.setup(mLauncher, mLauncher, null, + dragOverView.getMeasuredWidth(), dragOverView.getPaddingTop()); - if (!dragObject.accessibleDrag) { - mFolderCreationAlarm.setOnAlarmListener(listener); - mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT); - } else { - listener.onAlarm(mFolderCreationAlarm); - } + // The full preview background should appear behind the icon + mFolderCreateBg.isClipping = false; + + mFolderCreateBg.animateToAccept(mDragTargetLayout, mTargetCell[0], mTargetCell[1]); + mDragTargetLayout.clearDragOutlines(); + setDragMode(DRAG_MODE_CREATE_FOLDER); if (dragObject.stateAnnouncer != null) { dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper @@ -2330,8 +2312,8 @@ public class Workspace extends PagedView if (willAddToFolder && mDragMode == DRAG_MODE_NONE) { mDragOverFolderIcon = ((FolderIcon) dragOverView); mDragOverFolderIcon.onDragEnter(info); - if (targetLayout != null) { - targetLayout.clearDragOutlines(); + if (mDragTargetLayout != null) { + mDragTargetLayout.clearDragOutlines(); } setDragMode(DRAG_MODE_ADD_TO_FOLDER); @@ -2350,33 +2332,6 @@ public class Workspace extends PagedView } } - class FolderCreationAlarmListener implements OnAlarmListener { - final CellLayout layout; - final int cellX; - final int cellY; - - final PreviewBackground bg = new PreviewBackground(); - - public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) { - this.layout = layout; - this.cellX = cellX; - this.cellY = cellY; - - BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY); - bg.setup(mLauncher, mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop()); - - // The full preview background should appear behind the icon - bg.isClipping = false; - } - - public void onAlarm(Alarm alarm) { - mFolderCreateBg = bg; - mFolderCreateBg.animateToAccept(layout, cellX, cellY); - layout.clearDragOutlines(); - setDragMode(DRAG_MODE_CREATE_FOLDER); - } - } - class ReorderAlarmListener implements OnAlarmListener { final float[] dragViewCenter; final int minSpanX, minSpanY, spanX, spanY; diff --git a/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java b/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java index f8df5d7be7..0d7df2b44d 100644 --- a/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java +++ b/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java @@ -16,7 +16,9 @@ package com.android.launcher3.accessibility; +import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.OnHierarchyChangeListener; import com.android.launcher3.CellLayout; import com.android.launcher3.DropTarget.DragObject; @@ -24,36 +26,55 @@ import com.android.launcher3.Launcher; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; +import java.util.function.Function; + /** * Utility listener to enable/disable accessibility drag flags for a ViewGroup * containing CellLayouts */ -public class AccessibleDragListenerAdapter implements DragListener { +public class AccessibleDragListenerAdapter implements DragListener, OnHierarchyChangeListener { private final ViewGroup mViewGroup; - private final int mDragType; + private final Function mDelegateFactory; /** - * @param parent - * @param dragType either {@link CellLayout#WORKSPACE_ACCESSIBILITY_DRAG} or - * {@link CellLayout#FOLDER_ACCESSIBILITY_DRAG} + * @param parent the viewgroup containing all the children + * @param delegateFactory function to create no delegates */ - public AccessibleDragListenerAdapter(ViewGroup parent, int dragType) { + public AccessibleDragListenerAdapter(ViewGroup parent, + Function delegateFactory) { mViewGroup = parent; - mDragType = dragType; + mDelegateFactory = delegateFactory; } @Override public void onDragStart(DragObject dragObject, DragOptions options) { + mViewGroup.setOnHierarchyChangeListener(this); enableAccessibleDrag(true); } @Override public void onDragEnd() { + mViewGroup.setOnHierarchyChangeListener(null); enableAccessibleDrag(false); Launcher.getLauncher(mViewGroup.getContext()).getDragController().removeDragListener(this); } + + @Override + public void onChildViewAdded(View parent, View child) { + if (parent == mViewGroup) { + setEnableForLayout((CellLayout) child, true); + } + } + + @Override + public void onChildViewRemoved(View parent, View child) { + if (parent == mViewGroup) { + setEnableForLayout((CellLayout) child, false); + } + } + protected void enableAccessibleDrag(boolean enable) { for (int i = 0; i < mViewGroup.getChildCount(); i++) { setEnableForLayout((CellLayout) mViewGroup.getChildAt(i), enable); @@ -61,6 +82,6 @@ public class AccessibleDragListenerAdapter implements DragListener { } protected final void setEnableForLayout(CellLayout layout, boolean enable) { - layout.enableAccessibleDrag(enable, mDragType); + layout.setDragAndDropAccessibilityDelegate(enable ? mDelegateFactory.apply(layout) : null); } } diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java index 117296d5f6..ddb547ffb7 100644 --- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java @@ -19,24 +19,26 @@ package com.android.launcher3.accessibility; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; +import android.view.View.OnHoverListener; import android.view.accessibility.AccessibilityEvent; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.customview.widget.ExploreByTouchHelper; + import com.android.launcher3.CellLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; import java.util.List; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.customview.widget.ExploreByTouchHelper; - /** * Helper class to make drag-and-drop in a {@link CellLayout} accessible. */ public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper - implements OnClickListener { + implements OnClickListener, OnHoverListener { protected static final int INVALID_POSITION = -1; private static final int[] sTempArray = new int[2]; @@ -123,6 +125,11 @@ public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHel node.setFocusable(true); } + @Override + public boolean onHover(View view, MotionEvent motionEvent) { + return dispatchHoverEvent(motionEvent); + } + protected abstract String getLocationDescriptionForIconDrop(int id); protected abstract String getConfirmationForIconDrop(int id); diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 0b439ec362..6f7f8e6a29 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -7,6 +7,7 @@ import static com.android.launcher3.LauncherState.NORMAL; import android.app.AlertDialog; import android.appwidget.AppWidgetProviderInfo; import android.content.DialogInterface; +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -400,11 +401,11 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme Rect pos = new Rect(); mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); - mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY()); mLauncher.getDragController().addDragListener(this); DragOptions options = new DragOptions(); options.isAccessibleDrag = true; + options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY()); ItemLongClickListener.beginDrag(item, mLauncher, info, options); } diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java index 75693c63d0..9b91a1de67 100644 --- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java @@ -26,7 +26,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; -import android.os.SystemClock; import android.util.Log; import android.view.DragEvent; import android.view.View; @@ -63,7 +62,6 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS protected Launcher mLauncher; private DragController mDragController; - private long mDragStartTime; public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) { mPreviewRect = previewRect; @@ -102,7 +100,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS return false; } } - return mDragController.onDragEvent(mDragStartTime, event); + return mDragController.onDragEvent(event); } protected boolean onDragStart(DragEvent event) { @@ -118,7 +116,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS Point downPos = new Point((int) event.getX(), (int) event.getY()); DragOptions options = new DragOptions(); - options.systemDndStartPoint = downPos; + options.simulatedDndStartPoint = downPos; options.preDragCondition = preDragCondition; // We use drag event position as the screenPos for the preview image. Since mPreviewRect @@ -128,7 +126,6 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS // to source window. createDragHelper().startDrag(new Rect(mPreviewRect), mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options); - mDragStartTime = SystemClock.uptimeMillis(); return true; } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index de7bc6d0e2..15397474d4 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -43,7 +43,6 @@ import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.util.ItemInfoMatcher; -import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; import com.android.launcher3.util.UiThreadHelper; @@ -61,8 +60,8 @@ public class DragController implements DragDriver.EventListener, TouchController */ private static final int DEEP_PRESS_DISTANCE_FACTOR = 3; - @Thunk Launcher mLauncher; - private FlingToDeleteHelper mFlingToDeleteHelper; + private final Launcher mLauncher; + private final FlingToDeleteHelper mFlingToDeleteHelper; // temporaries to avoid gc thrash private Rect mRectTemp = new Rect(); @@ -77,11 +76,12 @@ public class DragController implements DragDriver.EventListener, TouchController /** Options controlling the drag behavior. */ private DragOptions mOptions; - /** X coordinate of the down event. */ - private int mMotionDownX; + /** Coordinate for motion down event */ + private final Point mMotionDown = new Point(); + /** Coordinate for last touch event **/ + private final Point mLastTouch = new Point(); - /** Y coordinate of the down event. */ - private int mMotionDownY; + private final Point mTmpPoint = new Point(); private DropTarget.DragObject mDragObject; @@ -96,12 +96,9 @@ public class DragController implements DragDriver.EventListener, TouchController private DropTarget mLastDropTarget; - private final int[] mLastTouch = new int[2]; - private long mLastTouchUpTime = -1; private int mLastTouchClassification; private int mDistanceSinceScroll = 0; - private int mTmpPoint[] = new int[2]; private Rect mDragLayerRect = new Rect(); private boolean mIsInPreDrag; @@ -159,13 +156,13 @@ public class DragController implements DragDriver.EventListener, TouchController AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE); mOptions = options; - if (mOptions.systemDndStartPoint != null) { - mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x; - mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y; + if (mOptions.simulatedDndStartPoint != null) { + mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x; + mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y; } - final int registrationX = mMotionDownX - dragLayerX; - final int registrationY = mMotionDownY - dragLayerY; + final int registrationX = mMotionDown.x - dragLayerX; + final int registrationY = mMotionDown.y - dragLayerY; final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; @@ -184,17 +181,13 @@ public class DragController implements DragDriver.EventListener, TouchController registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps); dragView.setItemInfo(dragInfo); mDragObject.dragComplete = false; - if (mOptions.isAccessibleDrag) { - // For an accessible drag, we assume the view is being dragged from the center. - mDragObject.xOffset = b.getWidth() / 2; - mDragObject.yOffset = b.getHeight() / 2; - mDragObject.accessibleDrag = true; - } else { - mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); - mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); - mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); - mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions); + mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft); + mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop); + + mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent); + if (!mOptions.isAccessibleDrag) { + mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); } mDragObject.dragSource = source; @@ -210,7 +203,7 @@ public class DragController implements DragDriver.EventListener, TouchController } mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - dragView.show(mLastTouch[0], mLastTouch[1]); + dragView.show(mLastTouch.x, mLastTouch.y); mDistanceSinceScroll = 0; if (!mIsInPreDrag) { @@ -219,7 +212,7 @@ public class DragController implements DragDriver.EventListener, TouchController mOptions.preDragCondition.onPreDragStart(mDragObject); } - handleMoveEvent(mLastTouch[0], mLastTouch[1]); + handleMoveEvent(mLastTouch.x, mLastTouch.y); mLauncher.getUserEventDispatcher().resetActionDurationMillis(); return dragView; } @@ -336,7 +329,7 @@ public class DragController implements DragDriver.EventListener, TouchController } } }; - mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration); + mDragObject.dragView.animateTo(mMotionDown.x, mMotionDown.y, onCompleteRunnable, duration); } private void callOnDragEnd() { @@ -365,30 +358,17 @@ public class DragController implements DragDriver.EventListener, TouchController /** * Clamps the position to the drag layer bounds. */ - private int[] getClampedDragLayerPos(float x, float y) { + private Point getClampedDragLayerPos(float x, float y) { mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect); - mTmpPoint[0] = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1)); - mTmpPoint[1] = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1)); + mTmpPoint.x = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1)); + mTmpPoint.y = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1)); return mTmpPoint; } - public long getLastGestureUpTime() { - if (mDragDriver != null) { - return System.currentTimeMillis(); - } else { - return mLastTouchUpTime; - } - } - - public void resetLastGestureUpTime() { - mLastTouchUpTime = -1; - } - @Override public void onDriverDragMove(float x, float y) { - final int[] dragLayerPos = getClampedDragLayerPos(x, y); - - handleMoveEvent(dragLayerPos[0], dragLayerPos[1]); + Point dragLayerPos = getClampedDragLayerPos(x, y); + handleMoveEvent(dragLayerPos.x, dragLayerPos.y); } @Override @@ -422,53 +402,38 @@ public class DragController implements DragDriver.EventListener, TouchController /** * Call this from a drag source view. */ + @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (mOptions != null && mOptions.isAccessibleDrag) { return false; } - // Update the velocity tracker - mFlingToDeleteHelper.recordMotionEvent(ev); + Point dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); + mLastTouch.set(dragLayerPos.x, dragLayerPos.y); + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + // Remember location of down touch + mMotionDown.set(dragLayerPos.x, dragLayerPos.y); + } - final int action = ev.getAction(); - final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); - final int dragLayerX = dragLayerPos[0]; - final int dragLayerY = dragLayerPos[1]; - mLastTouch[0] = dragLayerX; - mLastTouch[1] = dragLayerY; if (ATLEAST_Q) { mLastTouchClassification = ev.getClassification(); } - - switch (action) { - case MotionEvent.ACTION_DOWN: - // Remember location of down touch - mMotionDownX = dragLayerX; - mMotionDownY = dragLayerY; - break; - case MotionEvent.ACTION_UP: - mLastTouchUpTime = System.currentTimeMillis(); - break; - } - return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev); } /** * Call this from a drag source view. */ - public boolean onDragEvent(long dragStartTime, DragEvent event) { - mFlingToDeleteHelper.recordDragEvent(dragStartTime, event); - return mDragDriver != null && mDragDriver.onDragEvent(event); + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + return mDragDriver != null && mDragDriver.onTouchEvent(ev); } /** - * Call this from a drag view. + * Call this from a drag source view. */ - public void onDragViewAnimationEnd() { - if (mDragDriver != null) { - mDragDriver.onDragViewAnimationEnd(); - } + public boolean onDragEvent(DragEvent event) { + return mDragDriver != null && mDragDriver.onDragEvent(event); } /** @@ -493,9 +458,8 @@ public class DragController implements DragDriver.EventListener, TouchController checkTouchMove(dropTarget); // Check if we are hovering over the scroll areas - mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y); - mLastTouch[0] = x; - mLastTouch[1] = y; + mDistanceSinceScroll += Math.hypot(mLastTouch.x - x, mLastTouch.y - y); + mLastTouch.set(x, y); int distanceDragged = mDistanceSinceScroll; if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) { @@ -513,7 +477,7 @@ public class DragController implements DragDriver.EventListener, TouchController public void forceTouchMove() { int[] dummyCoordinates = mCoordinatesTemp; - DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates); + DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, dummyCoordinates); mDragObject.x = dummyCoordinates[0]; mDragObject.y = dummyCoordinates[1]; checkTouchMove(dropTarget); @@ -536,44 +500,6 @@ public class DragController implements DragDriver.EventListener, TouchController mLastDropTarget = dropTarget; } - /** - * Call this from a drag source view. - */ - public boolean onControllerTouchEvent(MotionEvent ev) { - if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) { - return false; - } - - // Update the velocity tracker - mFlingToDeleteHelper.recordMotionEvent(ev); - - final int action = ev.getAction(); - final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); - final int dragLayerX = dragLayerPos[0]; - final int dragLayerY = dragLayerPos[1]; - - switch (action) { - case MotionEvent.ACTION_DOWN: - // Remember where the motion event started - mMotionDownX = dragLayerX; - mMotionDownY = dragLayerY; - break; - } - - return mDragDriver.onTouchEvent(ev); - } - - /** - * Since accessible drag and drop won't cause the same sequence of touch events, we manually - * inject the appropriate state which would have been otherwise initiated via touch events. - */ - public void prepareAccessibleDrag(int x, int y) { - mMotionDownX = x; - mMotionDownY = y; - mLastTouch[0] = x; - mLastTouch[1] = y; - } - /** * As above, since accessible drag and drop won't cause the same sequence of touch events, * we manually ensure appropriate drag and drop events get emulated for accessible drag. diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java index 87461d57fe..d4ce3080c0 100644 --- a/src/com/android/launcher3/dragndrop/DragDriver.java +++ b/src/com/android/launcher3/dragndrop/DragDriver.java @@ -16,19 +16,19 @@ package com.android.launcher3.dragndrop; -import android.content.Context; -import android.util.Log; +import android.os.SystemClock; import android.view.DragEvent; import android.view.MotionEvent; -import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.testing.TestProtocol; +import java.util.function.Consumer; /** * Base class for driving a drag/drop operation. */ public abstract class DragDriver { + protected final EventListener mEventListener; + protected final Consumer mSecondaryEventConsumer; public interface EventListener { void onDriverDragMove(float x, float y); @@ -37,131 +37,175 @@ public abstract class DragDriver { void onDriverDragCancel(); } - public DragDriver(EventListener eventListener) { + public DragDriver(EventListener eventListener, Consumer sec) { mEventListener = eventListener; + mSecondaryEventConsumer = sec; } /** - * Handles ending of the DragView animation. + * Called to handle system touch event */ - public void onDragViewAnimationEnd() { } - public boolean onTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); - - switch (action) { - case MotionEvent.ACTION_MOVE: - mEventListener.onDriverDragMove(ev.getX(), ev.getY()); - break; - case MotionEvent.ACTION_UP: - mEventListener.onDriverDragMove(ev.getX(), ev.getY()); - mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); - break; - case MotionEvent.ACTION_CANCEL: - mEventListener.onDriverDragCancel(); - break; - } - - return true; + return false; } - public abstract boolean onDragEvent (DragEvent event); - - + /** + * Called to handle system touch intercept event + */ public boolean onInterceptTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); - - switch (action) { - case MotionEvent.ACTION_UP: - mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); - break; - case MotionEvent.ACTION_CANCEL: - mEventListener.onDriverDragCancel(); - break; - } - - return true; + return false; } - public static DragDriver create(Context context, DragController dragController, - DragObject dragObject, DragOptions options) { - if (options.systemDndStartPoint != null) { - return new SystemDragDriver(dragController, context, dragObject); + /** + * Called to handle system drag event + */ + public boolean onDragEvent(DragEvent event) { + return false; + } + + /** + * Created a driver for handing the actual events + */ + public static DragDriver create(DragController dragController, DragOptions options, + Consumer sec) { + if (options.simulatedDndStartPoint != null) { + if (options.isAccessibleDrag) { + return null; + } + return new SystemDragDriver(dragController, sec); } else { - return new InternalDragDriver(dragController); + return new InternalDragDriver(dragController, sec); + } + } + + /** + * Class for driving a system (i.e. framework) drag/drop operation. + */ + static class SystemDragDriver extends DragDriver { + + private final long mDragStartTime; + float mLastX = 0; + float mLastY = 0; + + SystemDragDriver(DragController dragController, Consumer sec) { + super(dragController, sec); + mDragStartTime = SystemClock.uptimeMillis(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return false; + } + + /** + * It creates a temporary {@link MotionEvent} object for secondary consumer + */ + private void simulateSecondaryMotionEvent(DragEvent event) { + final int motionAction; + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + motionAction = MotionEvent.ACTION_DOWN; + break; + case DragEvent.ACTION_DRAG_LOCATION: + motionAction = MotionEvent.ACTION_MOVE; + break; + case DragEvent.ACTION_DRAG_ENDED: + motionAction = MotionEvent.ACTION_UP; + break; + default: + return; + } + MotionEvent emulatedEvent = MotionEvent.obtain(mDragStartTime, + SystemClock.uptimeMillis(), motionAction, event.getX(), event.getY(), 0); + mSecondaryEventConsumer.accept(emulatedEvent); + emulatedEvent.recycle(); + } + + @Override + public boolean onDragEvent(DragEvent event) { + simulateSecondaryMotionEvent(event); + final int action = event.getAction(); + + switch (action) { + case DragEvent.ACTION_DRAG_STARTED: + mLastX = event.getX(); + mLastY = event.getY(); + return true; + + case DragEvent.ACTION_DRAG_ENTERED: + return true; + + case DragEvent.ACTION_DRAG_LOCATION: + mLastX = event.getX(); + mLastY = event.getY(); + mEventListener.onDriverDragMove(event.getX(), event.getY()); + return true; + + case DragEvent.ACTION_DROP: + mLastX = event.getX(); + mLastY = event.getY(); + mEventListener.onDriverDragMove(event.getX(), event.getY()); + mEventListener.onDriverDragEnd(mLastX, mLastY); + return true; + case DragEvent.ACTION_DRAG_EXITED: + mEventListener.onDriverDragExitWindow(); + return true; + + case DragEvent.ACTION_DRAG_ENDED: + mEventListener.onDriverDragCancel(); + return true; + + default: + return false; + } + } + } + + /** + * Class for driving an internal (i.e. not using framework) drag/drop operation. + */ + static class InternalDragDriver extends DragDriver { + InternalDragDriver(DragController dragController, Consumer sec) { + super(dragController, sec); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + mSecondaryEventConsumer.accept(ev); + final int action = ev.getAction(); + + switch (action) { + case MotionEvent.ACTION_MOVE: + mEventListener.onDriverDragMove(ev.getX(), ev.getY()); + break; + case MotionEvent.ACTION_UP: + mEventListener.onDriverDragMove(ev.getX(), ev.getY()); + mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); + break; + case MotionEvent.ACTION_CANCEL: + mEventListener.onDriverDragCancel(); + break; + } + + return true; + } + + + public boolean onInterceptTouchEvent(MotionEvent ev) { + mSecondaryEventConsumer.accept(ev); + final int action = ev.getAction(); + + switch (action) { + case MotionEvent.ACTION_UP: + mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); + break; + case MotionEvent.ACTION_CANCEL: + mEventListener.onDriverDragCancel(); + break; + } + return true; } } } -/** - * Class for driving a system (i.e. framework) drag/drop operation. - */ -class SystemDragDriver extends DragDriver { - float mLastX = 0; - float mLastY = 0; - - SystemDragDriver(DragController dragController, Context context, DragObject dragObject) { - super(dragController); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return false; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return false; - } - - @Override - public boolean onDragEvent (DragEvent event) { - final int action = event.getAction(); - - switch (action) { - case DragEvent.ACTION_DRAG_STARTED: - mLastX = event.getX(); - mLastY = event.getY(); - return true; - - case DragEvent.ACTION_DRAG_ENTERED: - return true; - - case DragEvent.ACTION_DRAG_LOCATION: - mLastX = event.getX(); - mLastY = event.getY(); - mEventListener.onDriverDragMove(event.getX(), event.getY()); - return true; - - case DragEvent.ACTION_DROP: - mLastX = event.getX(); - mLastY = event.getY(); - mEventListener.onDriverDragMove(event.getX(), event.getY()); - mEventListener.onDriverDragEnd(mLastX, mLastY); - return true; - case DragEvent.ACTION_DRAG_EXITED: - mEventListener.onDriverDragExitWindow(); - return true; - - case DragEvent.ACTION_DRAG_ENDED: - mEventListener.onDriverDragCancel(); - return true; - - default: - return false; - } - } -} - -/** - * Class for driving an internal (i.e. not using framework) drag/drop operation. - */ -class InternalDragDriver extends DragDriver { - InternalDragDriver(DragController dragController) { - super(dragController); - } - - @Override - public boolean onDragEvent (DragEvent event) { return false; } -} diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java index 2d19f36421..959602beb4 100644 --- a/src/com/android/launcher3/dragndrop/DragOptions.java +++ b/src/com/android/launcher3/dragndrop/DragOptions.java @@ -28,8 +28,11 @@ public class DragOptions { /** Whether or not an accessible drag operation is in progress. */ public boolean isAccessibleDrag = false; - /** Specifies the start location for the system DnD, null when using internal DnD */ - public Point systemDndStartPoint = null; + /** + * Specifies the start location for a simulated DnD (like system drag or accessibility drag), + * null when using internal DnD + */ + public Point simulatedDndStartPoint = null; /** Determines when a pre-drag should transition to a drag. By default, this is immediate. */ public PreDragCondition preDragCondition = null; diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 145885a458..7c76d34e03 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -19,8 +19,6 @@ package com.android.launcher3.dragndrop; import static com.android.launcher3.Utilities.getBadge; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.FloatArrayEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -146,15 +144,6 @@ public class DragView extends View implements LauncherStateManager.StateListener } }); - mAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!mAnimationCancelled) { - mDragController.onDragViewAnimationEnd(); - } - } - }); - mBitmap = bitmap; setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight())); diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java index 06b5c409de..7788f9366e 100644 --- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java +++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java @@ -17,8 +17,6 @@ package com.android.launcher3.dragndrop; import android.graphics.PointF; -import android.os.SystemClock; -import android.view.DragEvent; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; @@ -55,31 +53,6 @@ public class FlingToDeleteHelper { mVelocityTracker.addMovement(ev); } - /** - * Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object - * using {@param event} for tracking velocity. - */ - public void recordDragEvent(long dragStartTime, DragEvent event) { - final int motionAction; - switch (event.getAction()) { - case DragEvent.ACTION_DRAG_STARTED: - motionAction = MotionEvent.ACTION_DOWN; - break; - case DragEvent.ACTION_DRAG_LOCATION: - motionAction = MotionEvent.ACTION_MOVE; - break; - case DragEvent.ACTION_DRAG_ENDED: - motionAction = MotionEvent.ACTION_UP; - break; - default: - return; - } - MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(), - motionAction, event.getX(), event.getY(), 0); - recordMotionEvent(emulatedEvent); - emulatedEvent.recycle(); - } - public void releaseVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 5f496f4aba..c20717af29 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -85,6 +85,7 @@ import com.android.launcher3.Workspace; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; +import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; @@ -271,16 +272,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mDragController.addDragListener(this); if (options.isAccessibleDrag) { mDragController.addDragListener(new AccessibleDragListenerAdapter( - mContent, CellLayout.FOLDER_ACCESSIBILITY_DRAG) { - - @Override - protected void enableAccessibleDrag(boolean enable) { - super.enableAccessibleDrag(enable); - mFooter.setImportantForAccessibility(enable - ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } - }); + mContent, FolderAccessibilityHelper::new) { + @Override + protected void enableAccessibleDrag(boolean enable) { + super.enableAccessibleDrag(enable); + mFooter.setImportantForAccessibility(enable + ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } + }); } mLauncher.getWorkspace().beginDragShared(v, this, options);