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