• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22 import android.text.TextUtils;
23 import android.view.View;
24 
25 import com.android.launcher3.AppInfo;
26 import com.android.launcher3.CellLayout;
27 import com.android.launcher3.FolderInfo;
28 import com.android.launcher3.ItemInfo;
29 import com.android.launcher3.Launcher;
30 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
31 import com.android.launcher3.R;
32 import com.android.launcher3.ShortcutInfo;
33 import com.android.launcher3.dragndrop.DragLayer;
34 
35 /**
36  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
37  */
38 public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
39 
40     private final Rect mTempRect = new Rect();
41     private final int[] mTempCords = new int[2];
42 
WorkspaceAccessibilityHelper(CellLayout layout)43     public WorkspaceAccessibilityHelper(CellLayout layout) {
44         super(layout);
45     }
46 
47     /**
48      * Find the virtual view id corresponding to the top left corner of any drop region by which
49      * the passed id is contained. For an icon, this is simply
50      */
51     @Override
intersectsValidDropTarget(int id)52     protected int intersectsValidDropTarget(int id) {
53         int mCountX = mView.getCountX();
54         int mCountY = mView.getCountY();
55 
56         int x = id % mCountX;
57         int y = id / mCountX;
58         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
59 
60         if (dragInfo.dragType == DragType.WIDGET && !mView.acceptsWidget()) {
61             return INVALID_POSITION;
62         }
63 
64         if (dragInfo.dragType == DragType.WIDGET) {
65             // For a widget, every cell must be vacant. In addition, we will return any valid
66             // drop target by which the passed id is contained.
67             boolean fits = false;
68 
69             // These represent the amount that we can back off if we hit a problem. They
70             // get consumed as we move up and to the right, trying new regions.
71             int spanX = dragInfo.info.spanX;
72             int spanY = dragInfo.info.spanY;
73 
74             for (int m = 0; m < spanX; m++) {
75                 for (int n = 0; n < spanY; n++) {
76 
77                     fits = true;
78                     int x0 = x - m;
79                     int y0 = y - n;
80 
81                     if (x0 < 0 || y0 < 0) continue;
82 
83                     for (int i = x0; i < x0 + spanX; i++) {
84                         if (!fits) break;
85                         for (int j = y0; j < y0 + spanY; j++) {
86                             if (i >= mCountX || j >= mCountY || mView.isOccupied(i, j)) {
87                                 fits = false;
88                                 break;
89                             }
90                         }
91                     }
92                     if (fits) {
93                         return x0 + mCountX * y0;
94                     }
95                 }
96             }
97             return INVALID_POSITION;
98         } else {
99             // For an icon, we simply check the view directly below
100             View child = mView.getChildAt(x, y);
101             if (child == null || child == dragInfo.item) {
102                 // Empty cell. Good for an icon or folder.
103                 return id;
104             } else if (dragInfo.dragType != DragType.FOLDER) {
105                 // For icons, we can consider cells that have another icon or a folder.
106                 ItemInfo info = (ItemInfo) child.getTag();
107                 if (info instanceof AppInfo || info instanceof FolderInfo ||
108                         info instanceof ShortcutInfo) {
109                     return id;
110                 }
111             }
112             return INVALID_POSITION;
113         }
114     }
115 
116     @Override
getConfirmationForIconDrop(int id)117     protected String getConfirmationForIconDrop(int id) {
118         int x = id % mView.getCountX();
119         int y = id / mView.getCountX();
120         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
121 
122         View child = mView.getChildAt(x, y);
123         if (child == null || child == dragInfo.item) {
124             return mContext.getString(R.string.item_moved);
125         } else {
126             ItemInfo info = (ItemInfo) child.getTag();
127             if (info instanceof AppInfo || info instanceof ShortcutInfo) {
128                 return mContext.getString(R.string.folder_created);
129 
130             } else if (info instanceof FolderInfo) {
131                 return mContext.getString(R.string.added_to_folder);
132             }
133         }
134         return "";
135     }
136 
137     @Override
onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node)138     protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
139         super.onPopulateNodeForVirtualView(id, node);
140 
141 
142         // ExploreByTouchHelper does not currently handle view scale.
143         // Update BoundsInScreen to appropriate value.
144         DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
145         mTempCords[0] = mTempCords[1] = 0;
146         float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
147 
148         node.getBoundsInParent(mTempRect);
149         mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
150         mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
151         mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
152         mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
153         node.setBoundsInScreen(mTempRect);
154     }
155 
156     @Override
getLocationDescriptionForIconDrop(int id)157     protected String getLocationDescriptionForIconDrop(int id) {
158         int x = id % mView.getCountX();
159         int y = id / mView.getCountX();
160         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
161 
162         View child = mView.getChildAt(x, y);
163         if (child == null || child == dragInfo.item) {
164             return mView.getItemMoveDescription(x, y);
165         } else {
166             return getDescriptionForDropOver(child, mContext);
167         }
168     }
169 
getDescriptionForDropOver(View overChild, Context context)170     public static String getDescriptionForDropOver(View overChild, Context context) {
171         ItemInfo info = (ItemInfo) overChild.getTag();
172         if (info instanceof ShortcutInfo) {
173             return context.getString(R.string.create_folder_with, info.title);
174         } else if (info instanceof FolderInfo) {
175             if (TextUtils.isEmpty(info.title)) {
176                 // Find the first item in the folder.
177                 FolderInfo folder = (FolderInfo) info;
178                 ShortcutInfo firstItem = null;
179                 for (ShortcutInfo shortcut : folder.contents) {
180                     if (firstItem == null || firstItem.rank > shortcut.rank) {
181                         firstItem = shortcut;
182                     }
183                 }
184 
185                 if (firstItem != null) {
186                     return context.getString(R.string.add_to_folder_with_app, firstItem.title);
187                 }
188             }
189             return context.getString(R.string.add_to_folder, info.title);
190         }
191         return "";
192     }
193 }
194