DragAndDropAccessibilityDelegate.java revision e9b651eef1b9f3647eba94f833bff3fc52f5956b
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
17package com.android.launcher3.accessibility;
18
19import android.content.Context;
20import android.graphics.Rect;
21import android.os.Bundle;
22import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
23import android.support.v4.widget.ExploreByTouchHelper;
24import android.view.View;
25import android.view.View.OnClickListener;
26import android.view.accessibility.AccessibilityEvent;
27
28import com.android.launcher3.CellLayout;
29import com.android.launcher3.LauncherAccessibilityDelegate;
30import com.android.launcher3.LauncherAppState;
31import com.android.launcher3.R;
32
33import java.util.List;
34
35/**
36 * Helper class to make drag-and-drop in a {@link CellLayout} accessible.
37 */
38public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper
39        implements OnClickListener {
40    protected static final int INVALID_POSITION = -1;
41
42    private static final int[] sTempArray = new int[2];
43
44    protected final CellLayout mView;
45    protected final Context mContext;
46    protected final LauncherAccessibilityDelegate mDelegate;
47
48    private final Rect mTempRect = new Rect();
49
50    public DragAndDropAccessibilityDelegate(CellLayout forView) {
51        super(forView);
52        mView = forView;
53        mContext = mView.getContext();
54        mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate();
55    }
56
57    @Override
58    protected int getVirtualViewAt(float x, float y) {
59        if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) {
60            return INVALID_ID;
61        }
62        mView.pointToCellExact((int) x, (int) y, sTempArray);
63
64        // Map cell to id
65        int id = sTempArray[0] + sTempArray[1] * mView.getCountX();
66        return intersectsValidDropTarget(id);
67    }
68
69    /**
70     * @return the view id of the top left corner of a valid drop region or
71     * {@link #INVALID_POSITION} if there is no such valid region.
72     */
73    protected abstract int intersectsValidDropTarget(int id);
74
75    @Override
76    protected void getVisibleVirtualViews(List<Integer> virtualViews) {
77        // We create a virtual view for each cell of the grid
78        // The cell ids correspond to cells in reading order.
79        int nCells = mView.getCountX() * mView.getCountY();
80
81        for (int i = 0; i < nCells; i++) {
82            if (intersectsValidDropTarget(i) == i) {
83                virtualViews.add(i);
84            }
85        }
86    }
87
88    @Override
89    protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
90        if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) {
91            String confirmation = getConfirmationForIconDrop(viewId);
92            mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation);
93            return true;
94        }
95        return false;
96    }
97
98    @Override
99    public void onClick(View v) {
100        onPerformActionForVirtualView(getFocusedVirtualView(),
101                AccessibilityNodeInfoCompat.ACTION_CLICK, null);
102    }
103
104    @Override
105    protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) {
106        if (id == INVALID_ID) {
107            throw new IllegalArgumentException("Invalid virtual view id");
108        }
109        event.setContentDescription(mContext.getString(R.string.action_move_here));
110    }
111
112    @Override
113    protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
114        if (id == INVALID_ID) {
115            throw new IllegalArgumentException("Invalid virtual view id");
116        }
117
118        node.setContentDescription(getLocationDescriptionForIconDrop(id));
119        node.setBoundsInParent(getItemBounds(id));
120
121        node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
122        node.setClickable(true);
123        node.setFocusable(true);
124    }
125
126    protected abstract String getLocationDescriptionForIconDrop(int id);
127
128    protected abstract String getConfirmationForIconDrop(int id);
129
130    private Rect getItemBounds(int id) {
131        int cellX = id % mView.getCountX();
132        int cellY = id / mView.getCountX();
133        LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
134        mView.cellToRect(cellX, cellY, dragInfo.info.spanX, dragInfo.info.spanY, mTempRect);
135        return mTempRect;
136    }
137}