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