Merge "Enabling accessibility drag and drop in folder" into ub-launcher3-burnaby
This commit is contained in:
+15
-3
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -341,15 +346,6 @@ public class CellLayout extends ViewGroup {
|
||||
return super.dispatchHoverEvent(event);
|
||||
}
|
||||
|
||||
@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 ||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -658,6 +658,7 @@ public class DragController {
|
||||
mDragObject.y = coordinates[1];
|
||||
checkTouchMove(dropTarget);
|
||||
|
||||
dropTarget.prepareAccessibilityDrop();
|
||||
// Perform the drop
|
||||
drop(location[0], location[1]);
|
||||
endDrag();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user