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.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 22import android.text.TextUtils; 23import android.view.View; 24 25import com.android.launcher3.AppInfo; 26import com.android.launcher3.CellLayout; 27import com.android.launcher3.FolderInfo; 28import com.android.launcher3.ItemInfo; 29import com.android.launcher3.Launcher; 30import com.android.launcher3.R; 31import com.android.launcher3.ShortcutInfo; 32import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType; 33import com.android.launcher3.dragndrop.DragLayer; 34 35/** 36 * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace. 37 */ 38public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate { 39 40 private final Rect mTempRect = new Rect(); 41 private final int[] mTempCords = new int[2]; 42 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 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 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 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 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 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