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