summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/values/strings.xml18
-rw-r--r--src/com/android/launcher3/ButtonDropTarget.java3
-rw-r--r--src/com/android/launcher3/CellLayout.java288
-rw-r--r--src/com/android/launcher3/DragController.java1
-rw-r--r--src/com/android/launcher3/DropTarget.java2
-rw-r--r--src/com/android/launcher3/Folder.java35
-rw-r--r--src/com/android/launcher3/LauncherAccessibilityDelegate.java43
-rw-r--r--src/com/android/launcher3/Workspace.java21
-rw-r--r--src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java137
-rw-r--r--src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java50
-rw-r--r--src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java167
11 files changed, 478 insertions, 287 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b5e02f29d..a5a681a4f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -200,10 +200,13 @@
<!-- Strings for accessibility actions -->
<!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
- <string name="action_add_to_workspace">Add to workspace</string>
+ <string name="action_add_to_workspace">Add to home screen</string>
+
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
+ <string name="action_move_here">Move here</string>
<!-- Accessibility confirmation for item added to workspace [DO NOT TRANSLATE] -->
- <string name="item_added_to_workspace">Item added to workspace</string>
+ <string name="item_added_to_workspace">Item added to home screen</string>
<!-- Accessibility confirmation for item removed [DO NOT TRANSLATE] -->
<string name="item_removed">Item removed</string>
@@ -212,7 +215,13 @@
<string name="action_move">Move Item</string>
<!-- Accessibility description to move item to empty cell. [DO NOT TRANSLATE] -->
- <string name="move_to_empty_cell">Move to empty cell <xliff:g id="number" example="1">%1$s</xliff:g>, <xliff:g id="number" example="1">%2$s</xliff:g></string>
+ <string name="move_to_empty_cell">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g></string>
+
+ <!-- Accessibility description to move item inside a folder. [DO NOT TRANSLATE] -->
+ <string name="move_to_position">Move to position <xliff:g id="number" example="1">%1$s</xliff:g></string>
+
+ <!-- Accessibility description to move item to the hotseat. [DO NOT TRANSLATE] -->
+ <string name="move_to_hotseat_position">Move to favorites position <xliff:g id="number" example="1">%1$s</xliff:g></string>
<!-- Accessibility confirmation for item move [DO NOT TRANSLATE]-->
<string name="item_moved">Item moved</string>
@@ -220,6 +229,9 @@
<!-- Accessibility description to move item into an existing folder. [DO NOT TRANSLATE]-->
<string name="add_to_folder">Add to folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Accessibility description to move item into an existing folder containing an app. [DO NOT TRANSLATE]-->
+ <string name="add_to_folder_with_app">Add to folder with <xliff:g id="name" example="Messenger">%1$s</xliff:g></string>
+
<!-- Accessibility confirmation for item added to folder [DO NOT TRANSLATE] -->
<string name="added_to_folder">Item added to folder</string>
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index ee8710cd1..7cf002e40 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -226,6 +226,9 @@ public abstract class ButtonDropTarget extends TextView
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
+ @Override
+ public void prepareAccessibilityDrop() { }
+
@Thunk abstract void completeDrop(DragObject d);
@Override
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 83919809d..94f227595 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -35,11 +35,8 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -51,7 +48,9 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
-import com.android.launcher3.LauncherAccessibilityDelegate.DragType;
+import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
+import com.android.launcher3.accessibility.FolderAccessibilityHelper;
+import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -60,10 +59,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
import java.util.Stack;
public class CellLayout extends ViewGroup {
+ public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
+ public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
+
static final String TAG = "CellLayout";
private Launcher mLauncher;
@@ -178,12 +179,8 @@ public class CellLayout extends ViewGroup {
private final static Paint sPaint = new Paint();
// Related to accessible drag and drop
- DragAndDropAccessibilityDelegate mTouchHelper = new DragAndDropAccessibilityDelegate(this);
+ private DragAndDropAccessibilityDelegate mTouchHelper;
private boolean mUseTouchHelper = false;
- OnClickListener mOldClickListener = null;
- OnClickListener mOldWorkspaceListener = null;
- @Thunk int mDownX = 0;
- @Thunk int mDownY = 0;
public CellLayout(Context context) {
this(context, null);
@@ -311,14 +308,22 @@ public class CellLayout extends ViewGroup {
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public void enableAccessibleDrag(boolean enable) {
+ public void enableAccessibleDrag(boolean enable, int dragType) {
mUseTouchHelper = enable;
+ Log.e("HIGHRES", getParent() + " " + enable + " " + dragType, new Exception());
if (!enable) {
ViewCompat.setAccessibilityDelegate(this, null);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
setOnClickListener(mLauncher);
} 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);
@@ -342,15 +347,6 @@ public class CellLayout extends ViewGroup {
}
@Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mDownX = (int) event.getX();
- mDownY = (int) event.getY();
- }
- return super.dispatchTouchEvent(event);
- }
-
- @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mUseTouchHelper ||
(mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
@@ -359,252 +355,6 @@ public class CellLayout extends ViewGroup {
return false;
}
- class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper implements OnClickListener {
- private final Rect mTempRect = new Rect();
-
- public DragAndDropAccessibilityDelegate(View forView) {
- super(forView);
- }
-
- private int getViewIdAt(float x, float y) {
- if (x < 0 || y < 0 || x > getMeasuredWidth() || y > getMeasuredHeight()) {
- return ExploreByTouchHelper.INVALID_ID;
- }
-
- // Map coords to cell
- int cellX = (int) Math.floor(x / (mCellWidth + mWidthGap));
- int cellY = (int) Math.floor(y / (mCellHeight + mHeightGap));
-
- // Map cell to id
- int id = cellX * mCountY + cellY;
- return id;
- }
-
- @Override
- protected int getVirtualViewAt(float x, float y) {
- return nearestDropLocation(getViewIdAt(x, y));
- }
-
- protected int nearestDropLocation(int id) {
- int count = mCountX * mCountY;
- for (int delta = 0; delta < count; delta++) {
- if (id + delta <= (count - 1)) {
- int target = intersectsValidDropTarget(id + delta);
- if (target >= 0) {
- return target;
- }
- } else if (id - delta >= 0) {
- int target = intersectsValidDropTarget(id - delta);
- if (target >= 0) {
- return target;
- }
- }
- }
- return ExploreByTouchHelper.INVALID_ID;
- }
-
- /**
- * Find the virtual view id corresponding to the top left corner of any drop region by which
- * the passed id is contained. For an icon, this is simply
- *
- * @param id the id we're interested examining (ie. does it fit there?)
- * @return the view id of the top left corner of a valid drop region or -1 if there is no
- * such valid region. For the icon, this can just be -1 or id.
- */
- protected int intersectsValidDropTarget(int id) {
- LauncherAccessibilityDelegate delegate =
- LauncherAppState.getInstance().getAccessibilityDelegate();
- if (delegate == null) {
- return -1;
- }
-
- int y = id % mCountY;
- int x = id / mCountY;
- LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo();
-
- if (dragInfo.dragType == DragType.WIDGET) {
- // For a widget, every cell must be vacant. In addition, we will return any valid
- // drop target by which the passed id is contained.
- boolean fits = false;
-
- // These represent the amount that we can back off if we hit a problem. They
- // get consumed as we move up and to the right, trying new regions.
- int spanX = dragInfo.info.spanX;
- int spanY = dragInfo.info.spanY;
-
- for (int m = 0; m < spanX; m++) {
- for (int n = 0; n < spanY; n++) {
-
- fits = true;
- int x0 = x - m;
- int y0 = y - n;
-
- if (x0 < 0 || y0 < 0) continue;
-
- for (int i = x0; i < x0 + spanX; i++) {
- if (!fits) break;
- for (int j = y0; j < y0 + spanY; j++) {
- if (i >= mCountX || j >= mCountY || mOccupied[i][j]) {
- fits = false;
- break;
- }
- }
- }
- if (fits) {
- return x0 * mCountY + y0;
- }
- }
- }
- return -1;
- } else {
- // For an icon, we simply check the view directly below
- View child = getChildAt(x, y);
- if (child == null || child == dragInfo.item) {
- // Empty cell. Good for an icon or folder.
- return id;
- } else if (dragInfo.dragType != DragType.FOLDER) {
- // For icons, we can consider cells that have another icon or a folder.
- ItemInfo info = (ItemInfo) child.getTag();
- if (info instanceof AppInfo || info instanceof FolderInfo ||
- info instanceof ShortcutInfo) {
- return id;
- }
- }
- return -1;
- }
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViews) {
- // We create a virtual view for each cell of the grid
- // The cell ids correspond to cells in reading order.
- int nCells = mCountX * mCountY;
-
- for (int i = 0; i < nCells; i++) {
- if (intersectsValidDropTarget(i) >= 0) {
- virtualViews.add(i);
- }
- }
- }
-
- @Override
- protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
- LauncherAccessibilityDelegate delegate =
- LauncherAppState.getInstance().getAccessibilityDelegate();
- if (delegate == null) {
- return false;
- }
-
- if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
- String confirmation = getConfirmationForIconDrop(viewId);
- delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation);
- return true;
- }
- return false;
- }
-
- @Override
- public void onClick(View arg0) {
- LauncherAccessibilityDelegate delegate =
- LauncherAppState.getInstance().getAccessibilityDelegate();
- if (delegate == null) {
- return;
- }
-
- int viewId = getViewIdAt(mDownX, mDownY);
-
- String confirmation = getConfirmationForIconDrop(viewId);
- delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation);
- }
-
- @Override
- protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) {
- if (id == ExploreByTouchHelper.INVALID_ID) {
- throw new IllegalArgumentException("Invalid virtual view id");
- }
- // We're required to set something here.
- event.setContentDescription("");
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
- if (id == ExploreByTouchHelper.INVALID_ID) {
- throw new IllegalArgumentException("Invalid virtual view id");
- }
-
- node.setContentDescription(getLocationDescriptionForIconDrop(id));
- node.setBoundsInParent(getItemBounds(id));
-
- node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- node.setClickable(true);
- node.setFocusable(true);
- }
-
- private String getLocationDescriptionForIconDrop(int id) {
- LauncherAccessibilityDelegate delegate =
- LauncherAppState.getInstance().getAccessibilityDelegate();
- if (delegate == null) {
- return "";
- }
-
- int y = id % mCountY;
- int x = id / mCountY;
- LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo();
-
- Resources res = getContext().getResources();
- View child = getChildAt(x, y);
- if (child == null || child == dragInfo.item) {
- return res.getString(R.string.move_to_empty_cell, x + 1, y + 1);
- } else {
- ItemInfo info = (ItemInfo) child.getTag();
- if (info instanceof AppInfo || info instanceof ShortcutInfo) {
- return res.getString(R.string.create_folder_with, info.title);
- } else if (info instanceof FolderInfo) {
- return res.getString(R.string.add_to_folder, info.title);
- }
- }
- return "";
- }
-
- private String getConfirmationForIconDrop(int id) {
- LauncherAccessibilityDelegate delegate =
- LauncherAppState.getInstance().getAccessibilityDelegate();
- if (delegate == null) {
- return "";
- }
-
- int y = id % mCountY;
- int x = id / mCountY;
- LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo();
-
- Resources res = getContext().getResources();
- View child = getChildAt(x, y);
- if (child == null || child == dragInfo.item) {
- return res.getString(R.string.item_moved);
- } else {
- ItemInfo info = (ItemInfo) child.getTag();
- if (info instanceof AppInfo || info instanceof ShortcutInfo) {
- return res.getString(R.string.folder_created);
-
- } else if (info instanceof FolderInfo) {
- return res.getString(R.string.added_to_folder);
- }
- }
- return "";
- }
-
- private Rect getItemBounds(int id) {
- int cellY = id % mCountY;
- int cellX = id / mCountY;
- int x = getPaddingLeft() + (int) (cellX * (mCellWidth + mWidthGap));
- int y = getPaddingTop() + (int) (cellY * (mCellHeight + mHeightGap));
-
- Rect bounds = mTempRect;
- bounds.set(x, y, x + mCellWidth, y + mCellHeight);
- return bounds;
- }
- }
-
public void enableHardwareLayer(boolean hasLayer) {
mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
}
@@ -897,6 +647,10 @@ public class CellLayout extends ViewGroup {
mShortcutsAndWidgets.setIsHotseat(isHotseat);
}
+ public boolean isHotseat() {
+ return mIsHotseat;
+ }
+
public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
boolean markCells) {
final LayoutParams lp = params;
@@ -982,7 +736,7 @@ public class CellLayout extends ViewGroup {
* @param y Y coordinate of the point
* @param result Array of 2 ints to hold the x and y coordinate of the cell
*/
- void pointToCellExact(int x, int y, int[] result) {
+ public void pointToCellExact(int x, int y, int[] result) {
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 3b21c2b55..a8960996e 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -658,6 +658,7 @@ public class DragController {
mDragObject.y = coordinates[1];
checkTouchMove(dropTarget);
+ dropTarget.prepareAccessibilityDrop();
// Perform the drop
drop(location[0], location[1]);
endDrag();
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index c5cca3b28..3628e573d 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -183,6 +183,8 @@ public interface DropTarget {
*/
boolean acceptDrop(DragObject dragObject);
+ void prepareAccessibilityDrop();
+
// These methods are implemented in Views
void getHitRectRelativeToDragLayer(Rect outRect);
void getLocationInDragLayer(int[] loc);
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 03a9019e8..e0aeceae8 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -49,8 +49,10 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.launcher3.CellLayout.CellInfo;
import com.android.launcher3.DragController.DragListener;
import com.android.launcher3.FolderInfo.FolderListener;
+import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.util.Thunk;
@@ -63,7 +65,7 @@ import java.util.Collections;
*/
public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
- View.OnFocusChangeListener, DragListener, UninstallSource {
+ View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource {
private static final String TAG = "Launcher.Folder";
/**
@@ -237,7 +239,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public boolean onLongClick(View v) {
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return true;
+ return beginDrag(v, false);
+ }
+ private boolean beginDrag(View v, boolean accessible) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
ShortcutInfo item = (ShortcutInfo) tag;
@@ -245,7 +250,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return false;
}
- mLauncher.getWorkspace().beginDragShared(v, new Point(), this, false);
+ mLauncher.getWorkspace().beginDragShared(v, new Point(), this, accessible);
mCurrentDragInfo = item;
mEmptyCellRank = item.rank;
@@ -259,6 +264,20 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return true;
}
+ @Override
+ public void startDrag(CellInfo cellInfo, boolean accessible) {
+ beginDrag(cellInfo.cell, accessible);
+ }
+
+ @Override
+ public void enableAccessibleDrag(boolean enable) {
+ mLauncher.getSearchBar().enableAccessibleDrag(enable);
+ for (int i = 0; i < mContent.getChildCount(); i++) {
+ mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
+ }
+ mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
+ }
+
public boolean isEditingName() {
return mIsEditingName;
}
@@ -711,6 +730,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
+ /**
+ * When performing an accessibility drop, onDrop is sent immediately after onDragEnter. So we
+ * need to complete all transient states based on timers.
+ */
+ @Override
+ public void prepareAccessibilityDrop() {
+ if (mReorderAlarm.alarmPending()) {
+ mReorderAlarm.cancelAlarm();
+ mReorderAlarmListener.onAlarm(mReorderAlarm);
+ }
+ }
+
public void onDropCompleted(final View target, final DragObject d,
final boolean isFlingToDelete, final boolean success) {
if (mDeferDropAfterUninstall) {
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index bb32fa112..a527db423 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -27,23 +27,24 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
private static final int MOVE = R.id.action_move;
- enum DragType {
+ public enum DragType {
ICON,
FOLDER,
WIDGET
}
public static class DragInfo {
- DragType dragType;
- ItemInfo info;
- View item;
+ public DragType dragType;
+ public ItemInfo info;
+ public View item;
}
- private DragInfo mDragInfo = null;
-
private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
@Thunk final Launcher mLauncher;
+ private DragInfo mDragInfo = null;
+ private AccessibilityDragSource mDragSource = null;
+
public LauncherAccessibilityDelegate(Launcher launcher) {
mLauncher = launcher;
@@ -197,10 +198,23 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
Rect pos = new Rect();
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
-
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
- mLauncher.getWorkspace().enableAccessibleDrag(true);
- mLauncher.getWorkspace().startDrag(cellInfo, true);
+
+ Workspace workspace = mLauncher.getWorkspace();
+
+ Folder folder = workspace.getOpenFolder();
+ if (folder != null) {
+ if (folder.getItemsInReadingOrder().contains(item)) {
+ mDragSource = folder;
+ } else {
+ mLauncher.closeFolder();
+ }
+ }
+ if (mDragSource == null) {
+ mDragSource = workspace;
+ }
+ mDragSource.enableAccessibleDrag(true);
+ mDragSource.startDrag(cellInfo, true);
}
public boolean onBackPressed() {
@@ -218,7 +232,16 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
private void endAccessibleDrag() {
mDragInfo = null;
- mLauncher.getWorkspace().enableAccessibleDrag(false);
+ if (mDragSource != null) {
+ mDragSource.enableAccessibleDrag(false);
+ mDragSource = null;
+ }
+ }
+
+ public static interface AccessibilityDragSource {
+ void startDrag(CellLayout.CellInfo cellInfo, boolean accessible);
+
+ void enableAccessibleDrag(boolean enable);
}
/**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2efd20739..07d1c98f1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,6 +60,7 @@ import android.widget.TextView;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
+import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.compat.UserHandleCompat;
@@ -82,7 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class Workspace extends SmoothPagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
- Insettable, UninstallSource {
+ Insettable, UninstallSource, AccessibilityDragSource {
private static final String TAG = "Launcher.Workspace";
private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
@@ -125,6 +126,7 @@ public class Workspace extends SmoothPagedView
@Thunk Runnable mRemoveEmptyScreenRunnable;
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
+ @Thunk boolean mAddNewPageOnDrag = true;
/**
* CellInfo for the cell that is currently being dragged
@@ -390,7 +392,7 @@ public class Workspace extends SmoothPagedView
post(new Runnable() {
@Override
public void run() {
- if (mIsDragOccuring) {
+ if (mIsDragOccuring && mAddNewPageOnDrag) {
mDeferRemoveExtraEmptyScreen = false;
addExtraEmptyScreenOnDrag();
}
@@ -398,6 +400,9 @@ public class Workspace extends SmoothPagedView
});
}
+ public void setAddNewPageOnDrag(boolean addPage) {
+ mAddNewPageOnDrag = addPage;
+ }
public void deferRemoveExtraEmptyScreen() {
mDeferRemoveExtraEmptyScreen = true;
@@ -562,7 +567,7 @@ public class Workspace extends SmoothPagedView
LauncherAccessibilityDelegate delegate =
LauncherAppState.getInstance().getAccessibilityDelegate();
if (delegate != null && delegate.isInAccessibleDrag()) {
- newScreen.enableAccessibleDrag(true);
+ newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
return screenId;
}
@@ -1601,10 +1606,11 @@ public class Workspace extends SmoothPagedView
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @Override
public void enableAccessibleDrag(boolean enable) {
for (int i = 0; i < getChildCount(); i++) {
CellLayout child = (CellLayout) getChildAt(i);
- child.enableAccessibleDrag(enable);
+ child.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
if (enable) {
@@ -1615,7 +1621,8 @@ public class Workspace extends SmoothPagedView
setOnClickListener(mLauncher);
}
mLauncher.getSearchBar().enableAccessibleDrag(enable);
- mLauncher.getHotseat().getLayout().enableAccessibleDrag(enable);
+ mLauncher.getHotseat().getLayout()
+ .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
public boolean hasCustomContent() {
@@ -2262,6 +2269,7 @@ public class Workspace extends SmoothPagedView
startDrag(cellInfo, false);
}
+ @Override
public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) {
View child = cellInfo.cell;
@@ -2625,6 +2633,9 @@ public class Workspace extends SmoothPagedView
return false;
}
+ @Override
+ public void prepareAccessibilityDrop() { }
+
public void onDrop(final DragObject d) {
mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
CellLayout dropTargetLayout = mDropToLayout;
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
new file mode 100644
index 000000000..0f1724155
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.widget.ExploreByTouchHelper;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.LauncherAccessibilityDelegate;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+
+import java.util.List;
+
+/**
+ * Helper class to make drag-and-drop in a {@link CellLayout} accessible.
+ */
+public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper
+ implements OnClickListener {
+ protected static final int INVALID_POSITION = -1;
+
+ private static final int[] sTempArray = new int[2];
+
+ protected final CellLayout mView;
+ protected final Context mContext;
+ protected final LauncherAccessibilityDelegate mDelegate;
+
+ private final Rect mTempRect = new Rect();
+
+ public DragAndDropAccessibilityDelegate(CellLayout forView) {
+ super(forView);
+ mView = forView;
+ mContext = mView.getContext();
+ mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate();
+ }
+
+ @Override
+ protected int getVirtualViewAt(float x, float y) {
+ if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) {
+ return INVALID_ID;
+ }
+ mView.pointToCellExact((int) x, (int) y, sTempArray);
+
+ // Map cell to id
+ int id = sTempArray[0] + sTempArray[1] * mView.getCountX();
+ return intersectsValidDropTarget(id);
+ }
+
+ /**
+ * @return the view id of the top left corner of a valid drop region or
+ * {@link #INVALID_POSITION} if there is no such valid region.
+ */
+ protected abstract int intersectsValidDropTarget(int id);
+
+ @Override
+ protected void getVisibleVirtualViews(List<Integer> virtualViews) {
+ // We create a virtual view for each cell of the grid
+ // The cell ids correspond to cells in reading order.
+ int nCells = mView.getCountX() * mView.getCountY();
+
+ for (int i = 0; i < nCells; i++) {
+ if (intersectsValidDropTarget(i) == i) {
+ virtualViews.add(i);
+ }
+ }
+ }
+
+ @Override
+ protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
+ if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) {
+ String confirmation = getConfirmationForIconDrop(viewId);
+ mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onClick(View v) {
+ onPerformActionForVirtualView(getFocusedVirtualView(),
+ AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+ }
+
+ @Override
+ protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) {
+ if (id == INVALID_ID) {
+ throw new IllegalArgumentException("Invalid virtual view id");
+ }
+ event.setContentDescription(mContext.getString(R.string.action_move_here));
+ }
+
+ @Override
+ protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
+ if (id == INVALID_ID) {
+ throw new IllegalArgumentException("Invalid virtual view id");
+ }
+
+ node.setContentDescription(getLocationDescriptionForIconDrop(id));
+ node.setBoundsInParent(getItemBounds(id));
+
+ node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
+ node.setClickable(true);
+ node.setFocusable(true);
+ }
+
+ protected abstract String getLocationDescriptionForIconDrop(int id);
+
+ protected abstract String getConfirmationForIconDrop(int id);
+
+ private Rect getItemBounds(int id) {
+ int cellX = id % mView.getCountX();
+ int cellY = id / mView.getCountX();
+ LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
+ mView.cellToRect(cellX, cellY, dragInfo.info.spanX, dragInfo.info.spanY, mTempRect);
+ return mTempRect;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
new file mode 100644
index 000000000..fc105b4a4
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.FolderPagedView;
+import com.android.launcher3.R;
+
+/**
+ * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
+ */
+public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate {
+ private final int mStartPosition;
+
+ public FolderAccessibilityHelper(CellLayout layout) {
+ super(layout);
+ FolderPagedView parent = (FolderPagedView) layout.getParent();
+
+ int index = parent.indexOfChild(layout);
+ mStartPosition = 1 + index * layout.getCountX() * layout.getCountY();
+ }
+ @Override
+ protected int intersectsValidDropTarget(int id) {
+ return id;
+ }
+
+ @Override
+ protected String getLocationDescriptionForIconDrop(int id) {
+ return mContext.getString(R.string.move_to_position, id + mStartPosition);
+ }
+
+ @Override
+ protected String getConfirmationForIconDrop(int id) {
+ return mContext.getString(R.string.item_moved);
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
new file mode 100644
index 000000000..42e9e3c58
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAccessibilityDelegate;
+import com.android.launcher3.LauncherAccessibilityDelegate.DragType;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+
+/**
+ * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
+ */
+public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
+
+ public WorkspaceAccessibilityHelper(CellLayout layout) {
+ super(layout);
+ }
+
+ /**
+ * Find the virtual view id corresponding to the top left corner of any drop region by which
+ * the passed id is contained. For an icon, this is simply
+ */
+ @Override
+ protected int intersectsValidDropTarget(int id) {
+ int mCountX = mView.getCountX();
+ int mCountY = mView.getCountY();
+
+ int x = id % mCountX;
+ int y = id / mCountX;
+ LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
+
+ if (dragInfo.dragType == DragType.WIDGET && mView.isHotseat()) {
+ return INVALID_POSITION;
+ }
+
+ if (dragInfo.dragType == DragType.WIDGET) {
+ // For a widget, every cell must be vacant. In addition, we will return any valid
+ // drop target by which the passed id is contained.
+ boolean fits = false;
+
+ // These represent the amount that we can back off if we hit a problem. They
+ // get consumed as we move up and to the right, trying new regions.
+ int spanX = dragInfo.info.spanX;
+ int spanY = dragInfo.info.spanY;
+
+ for (int m = 0; m < spanX; m++) {
+ for (int n = 0; n < spanY; n++) {
+
+ fits = true;
+ int x0 = x - m;
+ int y0 = y - n;
+
+ if (x0 < 0 || y0 < 0) continue;
+
+ for (int i = x0; i < x0 + spanX; i++) {
+ if (!fits) break;
+ for (int j = y0; j < y0 + spanY; j++) {
+ if (i >= mCountX || j >= mCountY || mView.isOccupied(i, j)) {
+ fits = false;
+ break;
+ }
+ }
+ }
+ if (fits) {
+ return x0 + mCountX * y0;
+ }
+ }
+ }
+ return INVALID_POSITION;
+ } else {
+ // For an icon, we simply check the view directly below
+ View child = mView.getChildAt(x, y);
+ if (child == null || child == dragInfo.item) {
+ // Empty cell. Good for an icon or folder.
+ return id;
+ } else if (dragInfo.dragType != DragType.FOLDER) {
+ // For icons, we can consider cells that have another icon or a folder.
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (info instanceof AppInfo || info instanceof FolderInfo ||
+ info instanceof ShortcutInfo) {
+ return id;
+ }
+ }
+ return INVALID_POSITION;
+ }
+ }
+
+ @Override
+ protected String getConfirmationForIconDrop(int id) {
+ int x = id % mView.getCountX();
+ int y = id / mView.getCountX();
+ LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
+
+ View child = mView.getChildAt(x, y);
+ if (child == null || child == dragInfo.item) {
+ return mContext.getString(R.string.item_moved);
+ } else {
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (info instanceof AppInfo || info instanceof ShortcutInfo) {
+ return mContext.getString(R.string.folder_created);
+
+ } else if (info instanceof FolderInfo) {
+ return mContext.getString(R.string.added_to_folder);
+ }
+ }
+ return "";
+ }
+
+ @Override
+ protected String getLocationDescriptionForIconDrop(int id) {
+ int x = id % mView.getCountX();
+ int y = id / mView.getCountX();
+ LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
+
+ View child = mView.getChildAt(x, y);
+ if (child == null || child == dragInfo.item) {
+ if (mView.isHotseat()) {
+ return mContext.getString(R.string.move_to_hotseat_position, id + 1);
+ } else {
+ return mContext.getString(R.string.move_to_empty_cell, y + 1, x + 1);
+ }
+ } else {
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (info instanceof ShortcutInfo) {
+ return mContext.getString(R.string.create_folder_with, info.title);
+ } else if (info instanceof FolderInfo) {
+ if (TextUtils.isEmpty(info.title.toString().trim())) {
+ // Find the first item in the folder.
+ FolderInfo folder = (FolderInfo) info;
+ ShortcutInfo firstItem = null;
+ for (ShortcutInfo shortcut : folder.contents) {
+ if (firstItem == null || firstItem.rank > shortcut.rank) {
+ firstItem = shortcut;
+ }
+ }
+
+ if (firstItem != null) {
+ return mContext.getString(R.string.add_to_folder_with_app, firstItem.title);
+ }
+ }
+ return mContext.getString(R.string.add_to_folder, info.title);
+ }
+ }
+ return "";
+ }
+}