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