blob: a5852ba8280579a25324ab3637095dce49abf472 [file] [log] [blame]
Sunny Goyal83a8f042015-05-19 12:52:12 -07001package com.android.launcher3.accessibility;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002
Pinyao Ting8a739f92019-09-12 11:56:18 -07003import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
4
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07005import static com.android.launcher3.LauncherState.NORMAL;
Sunny Goyal384b5782021-02-09 22:50:02 -08006import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07007
Sunny Goyal9ca9c132015-04-29 14:57:22 -07008import android.appwidget.AppWidgetProviderInfo;
Sunny Goyal9b180102020-03-11 10:02:29 -07009import android.graphics.Point;
Adam Cohenc9735cf2015-01-23 16:11:55 -080010import android.graphics.Rect;
Sunny Goyal384b5782021-02-09 22:50:02 -080011import android.graphics.RectF;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080012import android.os.Bundle;
Sunny Goyal9ae77772015-04-29 16:30:23 -070013import android.os.Handler;
Sunny Goyal1a70cef2015-04-22 11:29:51 -070014import android.text.TextUtils;
Sunny Goyala9116722015-04-29 13:55:58 -070015import android.util.Log;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080016import android.util.SparseArray;
Sunny Goyal384b5782021-02-09 22:50:02 -080017import android.view.KeyEvent;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080018import android.view.View;
19import android.view.View.AccessibilityDelegate;
20import android.view.accessibility.AccessibilityNodeInfo;
21import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
22
Sunny Goyal83a8f042015-05-19 12:52:12 -070023import com.android.launcher3.AppWidgetResizeFrame;
Sunny Goyal3ffa64d2016-07-25 13:54:32 -070024import com.android.launcher3.BubbleTextView;
Sunny Goyal0236d0b2017-10-24 14:54:30 -070025import com.android.launcher3.ButtonDropTarget;
Sunny Goyal83a8f042015-05-19 12:52:12 -070026import com.android.launcher3.CellLayout;
Sunny Goyal94b510c2016-08-16 15:36:48 -070027import com.android.launcher3.DropTarget.DragObject;
Sunny Goyal83a8f042015-05-19 12:52:12 -070028import com.android.launcher3.Launcher;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070029import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyal83a8f042015-05-19 12:52:12 -070030import com.android.launcher3.PendingAddItemInfo;
31import com.android.launcher3.R;
Sunny Goyal83a8f042015-05-19 12:52:12 -070032import com.android.launcher3.Workspace;
Vadim Tryshevfedca432015-08-19 17:55:02 -070033import com.android.launcher3.dragndrop.DragController.DragListener;
Sunny Goyala52ecb02016-12-16 15:04:51 -080034import com.android.launcher3.dragndrop.DragOptions;
35import com.android.launcher3.folder.Folder;
Sunny Goyala4647b62021-02-02 13:45:34 -080036import com.android.launcher3.keyboard.KeyboardDragAndDropView;
Sunny Goyale396abf2020-04-06 15:11:17 -070037import com.android.launcher3.model.data.AppInfo;
38import com.android.launcher3.model.data.FolderInfo;
39import com.android.launcher3.model.data.ItemInfo;
40import com.android.launcher3.model.data.LauncherAppWidgetInfo;
41import com.android.launcher3.model.data.WorkspaceItemInfo;
Vadim Trysheva50d99f2018-05-04 17:58:29 -070042import com.android.launcher3.notification.NotificationListener;
Sunny Goyal384b5782021-02-09 22:50:02 -080043import com.android.launcher3.popup.ArrowPopup;
Mario Bertschlerc06af332017-03-28 12:23:22 -070044import com.android.launcher3.popup.PopupContainerWithArrow;
Vadim Trysheva50d99f2018-05-04 17:58:29 -070045import com.android.launcher3.touch.ItemLongClickListener;
Sunny Goyalefb7e842018-10-04 15:11:00 -070046import com.android.launcher3.util.IntArray;
Pinyao Ting49a3e692019-07-26 12:28:38 -070047import com.android.launcher3.util.ShortcutUtil;
Adam Cohen091440a2015-03-18 14:16:05 -070048import com.android.launcher3.util.Thunk;
Sunny Goyal384b5782021-02-09 22:50:02 -080049import com.android.launcher3.views.OptionsPopupView;
50import com.android.launcher3.views.OptionsPopupView.OptionItem;
Vadim Trysheva50d99f2018-05-04 17:58:29 -070051import com.android.launcher3.widget.LauncherAppWidgetHostView;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080052
53import java.util.ArrayList;
Sunny Goyal384b5782021-02-09 22:50:02 -080054import java.util.Collections;
55import java.util.List;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080056
Sunny Goyal45478022015-06-08 16:52:41 -070057public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080058
Sunny Goyala9116722015-04-29 13:55:58 -070059 private static final String TAG = "LauncherAccessibilityDelegate";
60
Sunny Goyal0236d0b2017-10-24 14:54:30 -070061 public static final int REMOVE = R.id.action_remove;
Sunny Goyal0236d0b2017-10-24 14:54:30 -070062 public static final int UNINSTALL = R.id.action_uninstall;
Samuel Fufa5cf3e862020-02-03 20:22:54 -080063 public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction;
Samuel Fufa6eaf9892020-04-01 11:40:40 -070064 public static final int PIN_PREDICTION = R.id.action_pin_prediction;
Winson Chung1054d4e2018-03-05 19:39:21 +000065 public static final int RECONFIGURE = R.id.action_reconfigure;
Sunny Goyal3ffa64d2016-07-25 13:54:32 -070066 protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
67 protected static final int MOVE = R.id.action_move;
68 protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
69 protected static final int RESIZE = R.id.action_resize;
Sunny Goyal66b24572016-09-21 15:57:55 -070070 public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
Vadim Trysheva50d99f2018-05-04 17:58:29 -070071 public static final int SHORTCUTS_AND_NOTIFICATIONS = R.id.action_shortcuts_and_notifications;
Adam Cohenc9735cf2015-01-23 16:11:55 -080072
Sunny Goyale9b651e2015-04-24 11:44:51 -070073 public enum DragType {
Adam Cohenc9735cf2015-01-23 16:11:55 -080074 ICON,
75 FOLDER,
76 WIDGET
77 }
78
79 public static class DragInfo {
Sunny Goyale9b651e2015-04-24 11:44:51 -070080 public DragType dragType;
81 public ItemInfo info;
82 public View item;
Adam Cohenc9735cf2015-01-23 16:11:55 -080083 }
84
Sunny Goyal384b5782021-02-09 22:50:02 -080085 protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
86 protected final Launcher mLauncher;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080087
Sunny Goyale9b651e2015-04-24 11:44:51 -070088 private DragInfo mDragInfo = null;
Sunny Goyale9b651e2015-04-24 11:44:51 -070089
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080090 public LauncherAccessibilityDelegate(Launcher launcher) {
91 mLauncher = launcher;
92
Sunny Goyal384b5782021-02-09 22:50:02 -080093 mActions.put(REMOVE, new LauncherAction(
94 REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X));
95 mActions.put(UNINSTALL, new LauncherAction(
96 UNINSTALL, R.string.uninstall_drop_target_label, KeyEvent.KEYCODE_U));
97 mActions.put(DISMISS_PREDICTION, new LauncherAction(DISMISS_PREDICTION,
98 R.string.dismiss_prediction_label, KeyEvent.KEYCODE_X));
99 mActions.put(RECONFIGURE, new LauncherAction(
100 RECONFIGURE, R.string.gadget_setup_text, KeyEvent.KEYCODE_E));
101 mActions.put(ADD_TO_WORKSPACE, new LauncherAction(
102 ADD_TO_WORKSPACE, R.string.action_add_to_workspace, KeyEvent.KEYCODE_P));
103 mActions.put(MOVE, new LauncherAction(
104 MOVE, R.string.action_move, KeyEvent.KEYCODE_M));
105 mActions.put(MOVE_TO_WORKSPACE, new LauncherAction(MOVE_TO_WORKSPACE,
106 R.string.action_move_to_workspace, KeyEvent.KEYCODE_P));
107 mActions.put(RESIZE, new LauncherAction(
108 RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
109 mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
110 R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
111 mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS,
112 R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S));
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800113 }
114
115 @Override
116 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
117 super.onInitializeAccessibilityNodeInfo(host, info);
Sunny Goyal384b5782021-02-09 22:50:02 -0800118 if (host.getTag() instanceof ItemInfo) {
119 ItemInfo item = (ItemInfo) host.getTag();
120
121 List<LauncherAction> actions = new ArrayList<>();
122 getSupportedActions(host, item, actions);
123 actions.forEach(la -> info.addAction(la.accessibilityAction));
124
125 if (!itemSupportsLongClick(host, item)) {
126 info.setLongClickable(false);
127 info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
128 }
129 }
Sunny Goyal3ffa64d2016-07-25 13:54:32 -0700130 }
131
Sunny Goyal384b5782021-02-09 22:50:02 -0800132 /**
133 * Adds all the accessibility actions that can be handled.
134 */
135 protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
Sunny Goyal66b24572016-09-21 15:57:55 -0700136 // If the request came from keyboard, do not add custom shortcuts as that is already
137 // exposed as a direct shortcut
Sunny Goyal384b5782021-02-09 22:50:02 -0800138 if (ShortcutUtil.supportsShortcuts(item)) {
139 out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
Vadim Trysheva50d99f2018-05-04 17:58:29 -0700140 ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
Sunny Goyal3ffa64d2016-07-25 13:54:32 -0700141 }
142
Sunny Goyal0236d0b2017-10-24 14:54:30 -0700143 for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
Winson Chung1054d4e2018-03-05 19:39:21 +0000144 if (target.supportsAccessibilityDrop(item, host)) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800145 out.add(mActions.get(target.getAccessibilityAction()));
Sunny Goyal0236d0b2017-10-24 14:54:30 -0700146 }
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700147 }
148
Sunny Goyal66b24572016-09-21 15:57:55 -0700149 // Do not add move actions for keyboard request as this uses virtual nodes.
Sunny Goyala4647b62021-02-02 13:45:34 -0800150 if (itemSupportsAccessibleDrag(item)) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800151 out.add(mActions.get(MOVE));
Sunny Goyal9ae77772015-04-29 16:30:23 -0700152
153 if (item.container >= 0) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800154 out.add(mActions.get(MOVE_TO_WORKSPACE));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700155 } else if (item instanceof LauncherAppWidgetInfo) {
156 if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800157 out.add(mActions.get(RESIZE));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700158 }
Sunny Goyal9ae77772015-04-29 16:30:23 -0700159 }
Adam Cohen6e92f052016-06-07 14:30:10 -0700160 }
161
162 if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800163 out.add(mActions.get(ADD_TO_WORKSPACE));
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800164 }
165 }
166
Sunny Goyal384b5782021-02-09 22:50:02 -0800167 /**
168 * Returns all the accessibility actions that can be handled by the host.
169 */
170 public static List<LauncherAction> getSupportedActions(Launcher launcher, View host) {
171 if (host == null || !(host.getTag() instanceof ItemInfo)) {
172 return Collections.emptyList();
173 }
174 PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
175 LauncherAccessibilityDelegate delegate = container != null
176 ? container.getAccessibilityDelegate() : launcher.getAccessibilityDelegate();
177 List<LauncherAction> result = new ArrayList<>();
178 delegate.getSupportedActions(host, (ItemInfo) host.getTag(), result);
179 return result;
180 }
181
Jon Mirandacafe9182020-03-25 11:46:11 -0700182 private boolean itemSupportsLongClick(View host, ItemInfo info) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800183 return PopupContainerWithArrow.canShow(host, info);
Jon Mirandacafe9182020-03-25 11:46:11 -0700184 }
185
vadimt5bc87ec2019-02-07 19:44:08 -0800186 private boolean itemSupportsAccessibleDrag(ItemInfo item) {
Sunny Goyal95899162019-03-27 16:03:06 -0700187 if (item instanceof WorkspaceItemInfo) {
vadimt5bc87ec2019-02-07 19:44:08 -0800188 // Support the action unless the item is in a context menu.
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700189 return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
vadimt5bc87ec2019-02-07 19:44:08 -0800190 }
191 return (item instanceof LauncherAppWidgetInfo)
192 || (item instanceof FolderInfo);
193 }
194
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800195 @Override
196 public boolean performAccessibilityAction(View host, int action, Bundle args) {
197 if ((host.getTag() instanceof ItemInfo)
Sunny Goyala4647b62021-02-02 13:45:34 -0800198 && performAction(host, (ItemInfo) host.getTag(), action, false)) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800199 return true;
200 }
201 return super.performAccessibilityAction(host, action, args);
202 }
203
Sunny Goyala4647b62021-02-02 13:45:34 -0800204 /**
205 * Performs the provided action on the host
206 */
Sunny Goyal384b5782021-02-09 22:50:02 -0800207 protected boolean performAction(final View host, final ItemInfo item, int action,
Sunny Goyala4647b62021-02-02 13:45:34 -0800208 boolean fromKeyboard) {
Adam Cohen732b1d42020-01-30 10:46:52 -0800209 if (action == ACTION_LONG_CLICK) {
Jon Mirandaff656742020-03-31 19:25:48 -0700210 if (PopupContainerWithArrow.canShow(host, item)) {
Adam Cohen732b1d42020-01-30 10:46:52 -0800211 // Long press should be consumed for workspace items, and it should invoke the
212 // Shortcuts / Notifications / Actions pop-up menu, and not start a drag as the
213 // standard long press path does.
214 PopupContainerWithArrow.showForIcon((BubbleTextView) host);
Pinyao Ting8a739f92019-09-12 11:56:18 -0700215 return true;
216 }
Sunny Goyal384b5782021-02-09 22:50:02 -0800217 } else if (action == MOVE) {
Sunny Goyala4647b62021-02-02 13:45:34 -0800218 return beginAccessibleDrag(host, item, fromKeyboard);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800219 } else if (action == ADD_TO_WORKSPACE) {
Sunny Goyala9116722015-04-29 13:55:58 -0700220 final int[] coordinates = new int[2];
Sunny Goyalefb7e842018-10-04 15:11:00 -0700221 final int screenId = findSpaceOnWorkspace(item, coordinates);
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700222 mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800223
224 @Override
Sunny Goyala9116722015-04-29 13:55:58 -0700225 public void run() {
226 if (item instanceof AppInfo) {
Sunny Goyal95899162019-03-27 16:03:06 -0700227 WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
Sunny Goyal43bf11d2017-02-02 13:52:53 -0800228 mLauncher.getModelWriter().addItemToDatabase(info,
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700229 Favorites.CONTAINER_DESKTOP,
Sunny Goyala9116722015-04-29 13:55:58 -0700230 screenId, coordinates[0], coordinates[1]);
231
Sunny Goyal384b5782021-02-09 22:50:02 -0800232 mLauncher.bindItems(Collections.singletonList(info), true);
Pinyao Tingc9074272019-11-11 11:51:44 -0800233 announceConfirmation(R.string.item_added_to_workspace);
Sunny Goyala9116722015-04-29 13:55:58 -0700234 } else if (item instanceof PendingAddItemInfo) {
235 PendingAddItemInfo info = (PendingAddItemInfo) item;
236 Workspace workspace = mLauncher.getWorkspace();
237 workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700238 mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
Sunny Goyala9116722015-04-29 13:55:58 -0700239 screenId, coordinates, info.spanX, info.spanY);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800240 }
Sunny Goyala9116722015-04-29 13:55:58 -0700241 }
242 });
243 return true;
Sunny Goyal9ae77772015-04-29 16:30:23 -0700244 } else if (action == MOVE_TO_WORKSPACE) {
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700245 Folder folder = Folder.getOpen(mLauncher);
246 folder.close(true);
Sunny Goyal95899162019-03-27 16:03:06 -0700247 WorkspaceItemInfo info = (WorkspaceItemInfo) item;
Sunny Goyalc52ba712016-04-05 15:59:05 -0700248 folder.getInfo().remove(info, false);
Sunny Goyal9ae77772015-04-29 16:30:23 -0700249
250 final int[] coordinates = new int[2];
Sunny Goyalefb7e842018-10-04 15:11:00 -0700251 final int screenId = findSpaceOnWorkspace(item, coordinates);
Sunny Goyal43bf11d2017-02-02 13:52:53 -0800252 mLauncher.getModelWriter().moveItemInDatabase(info,
Sunny Goyal384b5782021-02-09 22:50:02 -0800253 Favorites.CONTAINER_DESKTOP,
Sunny Goyal9ae77772015-04-29 16:30:23 -0700254 screenId, coordinates[0], coordinates[1]);
255
256 // Bind the item in next frame so that if a new workspace page was created,
257 // it will get laid out.
Sunny Goyal384b5782021-02-09 22:50:02 -0800258 new Handler().post(() -> {
259 mLauncher.bindItems(Collections.singletonList(item), true);
260 announceConfirmation(R.string.item_moved);
Sunny Goyal9ae77772015-04-29 16:30:23 -0700261 });
Sunny Goyal384b5782021-02-09 22:50:02 -0800262 return true;
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700263 } else if (action == RESIZE) {
264 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
Sunny Goyal384b5782021-02-09 22:50:02 -0800265 List<OptionItem> actions = getSupportedResizeActions(host, info);
266 Rect pos = new Rect();
267 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
Andy Wickham7d821d92021-04-01 16:04:44 -0700268 ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions, false);
Sunny Goyal384b5782021-02-09 22:50:02 -0800269 popup.requestFocus();
270 popup.setOnCloseCallback(host::requestFocus);
Sunny Goyal3ffa64d2016-07-25 13:54:32 -0700271 return true;
Sunny Goyal384b5782021-02-09 22:50:02 -0800272 } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
Tony Wickham540913e2017-01-23 11:47:51 -0800273 return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
Sunny Goyal0236d0b2017-10-24 14:54:30 -0700274 } else {
275 for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800276 if (dropTarget.supportsAccessibilityDrop(item, host)
277 && action == dropTarget.getAccessibilityAction()) {
Sunny Goyal0236d0b2017-10-24 14:54:30 -0700278 dropTarget.onAccessibilityDrop(host, item);
279 return true;
280 }
281 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800282 }
283 return false;
284 }
Adam Cohenc9735cf2015-01-23 16:11:55 -0800285
Sunny Goyal384b5782021-02-09 22:50:02 -0800286 private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
287 List<OptionItem> actions = new ArrayList<>();
Sunny Goyald3d8c952015-06-01 10:09:06 -0700288 AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
289 if (providerInfo == null) {
290 return actions;
291 }
292
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700293 CellLayout layout = (CellLayout) host.getParent().getParent();
294 if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
295 if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
296 layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
Andy Wickham7d821d92021-04-01 16:04:44 -0700297 actions.add(new OptionItem(mLauncher,
298 R.string.action_increase_width,
299 R.drawable.ic_widget_width_increase,
Sunny Goyal384b5782021-02-09 22:50:02 -0800300 IGNORE,
301 v -> performResizeAction(R.string.action_increase_width, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700302 }
303
304 if (info.spanX > info.minSpanX && info.spanX > 1) {
Andy Wickham7d821d92021-04-01 16:04:44 -0700305 actions.add(new OptionItem(mLauncher,
306 R.string.action_decrease_width,
307 R.drawable.ic_widget_width_decrease,
Sunny Goyal384b5782021-02-09 22:50:02 -0800308 IGNORE,
309 v -> performResizeAction(R.string.action_decrease_width, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700310 }
311 }
312
313 if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
314 if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
315 layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
Andy Wickham7d821d92021-04-01 16:04:44 -0700316 actions.add(new OptionItem(mLauncher,
317 R.string.action_increase_height,
318 R.drawable.ic_widget_height_increase,
Sunny Goyal384b5782021-02-09 22:50:02 -0800319 IGNORE,
320 v -> performResizeAction(R.string.action_increase_height, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700321 }
322
323 if (info.spanY > info.minSpanY && info.spanY > 1) {
Andy Wickham7d821d92021-04-01 16:04:44 -0700324 actions.add(new OptionItem(mLauncher,
325 R.string.action_decrease_height,
326 R.drawable.ic_widget_height_decrease,
Sunny Goyal384b5782021-02-09 22:50:02 -0800327 IGNORE,
328 v -> performResizeAction(R.string.action_decrease_height, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700329 }
330 }
331 return actions;
332 }
333
Sunny Goyal384b5782021-02-09 22:50:02 -0800334 private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700335 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
336 CellLayout layout = (CellLayout) host.getParent().getParent();
337 layout.markCellsAsUnoccupiedForView(host);
338
339 if (action == R.string.action_increase_width) {
340 if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
341 && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
342 || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
343 lp.cellX --;
344 info.cellX --;
345 }
346 lp.cellHSpan ++;
347 info.spanX ++;
348 } else if (action == R.string.action_decrease_width) {
349 lp.cellHSpan --;
350 info.spanX --;
351 } else if (action == R.string.action_increase_height) {
352 if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
353 lp.cellY --;
354 info.cellY --;
355 }
356 lp.cellVSpan ++;
357 info.spanY ++;
358 } else if (action == R.string.action_decrease_height) {
359 lp.cellVSpan --;
360 info.spanY --;
361 }
362
363 layout.markCellsAsOccupiedForView(host);
Pierre Barbier de Reuilleb2526fe2021-02-03 22:02:43 +0000364 AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
365 info.spanX, info.spanY);
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700366 host.requestLayout();
Sunny Goyal43bf11d2017-02-02 13:52:53 -0800367 mLauncher.getModelWriter().updateItemInDatabase(info);
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700368 announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
Sunny Goyal384b5782021-02-09 22:50:02 -0800369 return true;
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700370 }
371
Adam Cohen091440a2015-03-18 14:16:05 -0700372 @Thunk void announceConfirmation(int resId) {
Adam Cohenc9735cf2015-01-23 16:11:55 -0800373 announceConfirmation(mLauncher.getResources().getString(resId));
374 }
375
Adam Cohen091440a2015-03-18 14:16:05 -0700376 @Thunk void announceConfirmation(String confirmation) {
Adam Cohenc9735cf2015-01-23 16:11:55 -0800377 mLauncher.getDragLayer().announceForAccessibility(confirmation);
378
379 }
380
381 public boolean isInAccessibleDrag() {
382 return mDragInfo != null;
383 }
384
385 public DragInfo getDragInfo() {
386 return mDragInfo;
387 }
388
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700389 /**
390 * @param clickedTarget the actual view that was clicked
391 * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
392 * as the actual drop location otherwise the views center is used.
393 */
394 public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
Adam Cohenc9735cf2015-01-23 16:11:55 -0800395 String confirmation) {
396 if (!isInAccessibleDrag()) return;
397
398 int[] loc = new int[2];
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700399 if (dropLocation == null) {
400 loc[0] = clickedTarget.getWidth() / 2;
401 loc[1] = clickedTarget.getHeight() / 2;
402 } else {
403 loc[0] = dropLocation.centerX();
404 loc[1] = dropLocation.centerY();
405 }
Adam Cohenc9735cf2015-01-23 16:11:55 -0800406
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700407 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800408 mLauncher.getDragController().completeAccessibleDrag(loc);
409
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700410 if (!TextUtils.isEmpty(confirmation)) {
411 announceConfirmation(confirmation);
412 }
Adam Cohenc9735cf2015-01-23 16:11:55 -0800413 }
414
Sunny Goyala4647b62021-02-02 13:45:34 -0800415 private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
416 if (!itemSupportsAccessibleDrag(info)) {
417 return false;
418 }
419
Adam Cohenc9735cf2015-01-23 16:11:55 -0800420 mDragInfo = new DragInfo();
421 mDragInfo.info = info;
422 mDragInfo.item = item;
423 mDragInfo.dragType = DragType.ICON;
424 if (info instanceof FolderInfo) {
425 mDragInfo.dragType = DragType.FOLDER;
426 } else if (info instanceof LauncherAppWidgetInfo) {
427 mDragInfo.dragType = DragType.WIDGET;
428 }
429
Adam Cohenc9735cf2015-01-23 16:11:55 -0800430 Rect pos = new Rect();
431 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
Sunny Goyal94b510c2016-08-16 15:36:48 -0700432 mLauncher.getDragController().addDragListener(this);
433
434 DragOptions options = new DragOptions();
435 options.isAccessibleDrag = true;
Sunny Goyala4647b62021-02-02 13:45:34 -0800436 options.isKeyboardDrag = fromKeyboard;
Sunny Goyal9b180102020-03-11 10:02:29 -0700437 options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
Sunny Goyala4647b62021-02-02 13:45:34 -0800438
439 if (fromKeyboard) {
440 KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
441 .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
442 popup.showForIcon(item, info, options);
443 } else {
444 ItemLongClickListener.beginDrag(item, mLauncher, info, options);
445 }
446 return true;
Adam Cohenc9735cf2015-01-23 16:11:55 -0800447 }
448
Sunny Goyal45478022015-06-08 16:52:41 -0700449 @Override
Sunny Goyal94b510c2016-08-16 15:36:48 -0700450 public void onDragStart(DragObject dragObject, DragOptions options) {
Sunny Goyal45478022015-06-08 16:52:41 -0700451 // No-op
Adam Cohenc9735cf2015-01-23 16:11:55 -0800452 }
453
Sunny Goyal45478022015-06-08 16:52:41 -0700454 @Override
455 public void onDragEnd() {
456 mLauncher.getDragController().removeDragListener(this);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800457 mDragInfo = null;
Adam Cohenc9735cf2015-01-23 16:11:55 -0800458 }
Sunny Goyala9116722015-04-29 13:55:58 -0700459
460 /**
461 * Find empty space on the workspace and returns the screenId.
462 */
Sunny Goyalefb7e842018-10-04 15:11:00 -0700463 protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
Sunny Goyala9116722015-04-29 13:55:58 -0700464 Workspace workspace = mLauncher.getWorkspace();
Sunny Goyalefb7e842018-10-04 15:11:00 -0700465 IntArray workspaceScreens = workspace.getScreenOrder();
466 int screenId;
Sunny Goyala9116722015-04-29 13:55:58 -0700467
468 // First check if there is space on the current screen.
469 int screenIndex = workspace.getCurrentPage();
470 screenId = workspaceScreens.get(screenIndex);
471 CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex);
472
473 boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700474 screenIndex = 0;
Sunny Goyala9116722015-04-29 13:55:58 -0700475 while (!found && screenIndex < workspaceScreens.size()) {
476 screenId = workspaceScreens.get(screenIndex);
477 layout = (CellLayout) workspace.getPageAt(screenIndex);
478 found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
479 screenIndex++;
480 }
481
482 if (found) {
483 return screenId;
484 }
485
486 workspace.addExtraEmptyScreen();
487 screenId = workspace.commitExtraEmptyScreen();
488 layout = workspace.getScreenWithId(screenId);
489 found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
490
491 if (!found) {
492 Log.wtf(TAG, "Not enough space on an empty screen");
493 }
494 return screenId;
495 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700496
Sunny Goyal384b5782021-02-09 22:50:02 -0800497 public class LauncherAction {
498 public final int keyCode;
499 public final AccessibilityAction accessibilityAction;
500
501 private final LauncherAccessibilityDelegate mDelegate;
502
503 public LauncherAction(int id, int labelRes, int keyCode) {
504 this.keyCode = keyCode;
505 accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
506 mDelegate = LauncherAccessibilityDelegate.this;
507 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700508
509 /**
Sunny Goyal384b5782021-02-09 22:50:02 -0800510 * Invokes the action for the provided host
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700511 */
Sunny Goyal384b5782021-02-09 22:50:02 -0800512 public boolean invokeFromKeyboard(View host) {
513 if (host != null && host.getTag() instanceof ItemInfo) {
514 return mDelegate.performAction(
515 host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
516 } else {
517 return false;
518 }
519 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700520 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800521}