1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.accessibility; 18 19 import android.content.Context; 20 import android.graphics.Rect; 21 import android.os.Bundle; 22 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 23 import android.support.v4.widget.ExploreByTouchHelper; 24 import android.view.View; 25 import android.view.View.OnClickListener; 26 import android.view.accessibility.AccessibilityEvent; 27 28 import com.android.launcher3.CellLayout; 29 import com.android.launcher3.LauncherAppState; 30 import com.android.launcher3.R; 31 32 import java.util.List; 33 34 /** 35 * Helper class to make drag-and-drop in a {@link CellLayout} accessible. 36 */ 37 public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper 38 implements OnClickListener { 39 protected static final int INVALID_POSITION = -1; 40 41 private static final int[] sTempArray = new int[2]; 42 43 protected final CellLayout mView; 44 protected final Context mContext; 45 protected final LauncherAccessibilityDelegate mDelegate; 46 47 private final Rect mTempRect = new Rect(); 48 DragAndDropAccessibilityDelegate(CellLayout forView)49 public DragAndDropAccessibilityDelegate(CellLayout forView) { 50 super(forView); 51 mView = forView; 52 mContext = mView.getContext(); 53 mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate(); 54 } 55 56 @Override getVirtualViewAt(float x, float y)57 protected int getVirtualViewAt(float x, float y) { 58 if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) { 59 return INVALID_ID; 60 } 61 mView.pointToCellExact((int) x, (int) y, sTempArray); 62 63 // Map cell to id 64 int id = sTempArray[0] + sTempArray[1] * mView.getCountX(); 65 return intersectsValidDropTarget(id); 66 } 67 68 /** 69 * @return the view id of the top left corner of a valid drop region or 70 * {@link #INVALID_POSITION} if there is no such valid region. 71 */ intersectsValidDropTarget(int id)72 protected abstract int intersectsValidDropTarget(int id); 73 74 @Override getVisibleVirtualViews(List<Integer> virtualViews)75 protected void getVisibleVirtualViews(List<Integer> virtualViews) { 76 // We create a virtual view for each cell of the grid 77 // The cell ids correspond to cells in reading order. 78 int nCells = mView.getCountX() * mView.getCountY(); 79 80 for (int i = 0; i < nCells; i++) { 81 if (intersectsValidDropTarget(i) == i) { 82 virtualViews.add(i); 83 } 84 } 85 } 86 87 @Override onPerformActionForVirtualView(int viewId, int action, Bundle args)88 protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) { 89 if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) { 90 String confirmation = getConfirmationForIconDrop(viewId); 91 mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation); 92 return true; 93 } 94 return false; 95 } 96 97 @Override onClick(View v)98 public void onClick(View v) { 99 onPerformActionForVirtualView(getFocusedVirtualView(), 100 AccessibilityNodeInfoCompat.ACTION_CLICK, null); 101 } 102 103 @Override onPopulateEventForVirtualView(int id, AccessibilityEvent event)104 protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) { 105 if (id == INVALID_ID) { 106 throw new IllegalArgumentException("Invalid virtual view id"); 107 } 108 event.setContentDescription(mContext.getString(R.string.action_move_here)); 109 } 110 111 @Override onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node)112 protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) { 113 if (id == INVALID_ID) { 114 throw new IllegalArgumentException("Invalid virtual view id"); 115 } 116 117 node.setContentDescription(getLocationDescriptionForIconDrop(id)); 118 node.setBoundsInParent(getItemBounds(id)); 119 120 node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); 121 node.setClickable(true); 122 node.setFocusable(true); 123 } 124 getLocationDescriptionForIconDrop(int id)125 protected abstract String getLocationDescriptionForIconDrop(int id); 126 getConfirmationForIconDrop(int id)127 protected abstract String getConfirmationForIconDrop(int id); 128 getItemBounds(int id)129 private Rect getItemBounds(int id) { 130 int cellX = id % mView.getCountX(); 131 int cellY = id / mView.getCountX(); 132 LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo(); 133 mView.cellToRect(cellX, cellY, dragInfo.info.spanX, dragInfo.info.spanY, mTempRect); 134 return mTempRect; 135 } 136 }