blob: db7fd3feca8aa75255aae9e01b7fa992ee282168 [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);
268 ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions);
269 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)) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800297 actions.add(new OptionItem(
298 R.string.action_increase_width, R.drawable.ic_widget_width_increase,
299 IGNORE,
300 v -> performResizeAction(R.string.action_increase_width, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700301 }
302
303 if (info.spanX > info.minSpanX && info.spanX > 1) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800304 actions.add(new OptionItem(
305 R.string.action_decrease_width, R.drawable.ic_widget_width_decrease,
306 IGNORE,
307 v -> performResizeAction(R.string.action_decrease_width, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700308 }
309 }
310
311 if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
312 if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
313 layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800314 actions.add(new OptionItem(
315 R.string.action_increase_height, R.drawable.ic_widget_height_increase,
316 IGNORE,
317 v -> performResizeAction(R.string.action_increase_height, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700318 }
319
320 if (info.spanY > info.minSpanY && info.spanY > 1) {
Sunny Goyal384b5782021-02-09 22:50:02 -0800321 actions.add(new OptionItem(
322 R.string.action_decrease_height, R.drawable.ic_widget_height_decrease,
323 IGNORE,
324 v -> performResizeAction(R.string.action_decrease_height, host, info)));
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700325 }
326 }
327 return actions;
328 }
329
Sunny Goyal384b5782021-02-09 22:50:02 -0800330 private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700331 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
332 CellLayout layout = (CellLayout) host.getParent().getParent();
333 layout.markCellsAsUnoccupiedForView(host);
334
335 if (action == R.string.action_increase_width) {
336 if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
337 && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
338 || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
339 lp.cellX --;
340 info.cellX --;
341 }
342 lp.cellHSpan ++;
343 info.spanX ++;
344 } else if (action == R.string.action_decrease_width) {
345 lp.cellHSpan --;
346 info.spanX --;
347 } else if (action == R.string.action_increase_height) {
348 if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
349 lp.cellY --;
350 info.cellY --;
351 }
352 lp.cellVSpan ++;
353 info.spanY ++;
354 } else if (action == R.string.action_decrease_height) {
355 lp.cellVSpan --;
356 info.spanY --;
357 }
358
359 layout.markCellsAsOccupiedForView(host);
Pierre Barbier de Reuilleb2526fe2021-02-03 22:02:43 +0000360 AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
361 info.spanX, info.spanY);
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700362 host.requestLayout();
Sunny Goyal43bf11d2017-02-02 13:52:53 -0800363 mLauncher.getModelWriter().updateItemInDatabase(info);
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700364 announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
Sunny Goyal384b5782021-02-09 22:50:02 -0800365 return true;
Sunny Goyal9ca9c132015-04-29 14:57:22 -0700366 }
367
Adam Cohen091440a2015-03-18 14:16:05 -0700368 @Thunk void announceConfirmation(int resId) {
Adam Cohenc9735cf2015-01-23 16:11:55 -0800369 announceConfirmation(mLauncher.getResources().getString(resId));
370 }
371
Adam Cohen091440a2015-03-18 14:16:05 -0700372 @Thunk void announceConfirmation(String confirmation) {
Adam Cohenc9735cf2015-01-23 16:11:55 -0800373 mLauncher.getDragLayer().announceForAccessibility(confirmation);
374
375 }
376
377 public boolean isInAccessibleDrag() {
378 return mDragInfo != null;
379 }
380
381 public DragInfo getDragInfo() {
382 return mDragInfo;
383 }
384
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700385 /**
386 * @param clickedTarget the actual view that was clicked
387 * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
388 * as the actual drop location otherwise the views center is used.
389 */
390 public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
Adam Cohenc9735cf2015-01-23 16:11:55 -0800391 String confirmation) {
392 if (!isInAccessibleDrag()) return;
393
394 int[] loc = new int[2];
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700395 if (dropLocation == null) {
396 loc[0] = clickedTarget.getWidth() / 2;
397 loc[1] = clickedTarget.getHeight() / 2;
398 } else {
399 loc[0] = dropLocation.centerX();
400 loc[1] = dropLocation.centerY();
401 }
Adam Cohenc9735cf2015-01-23 16:11:55 -0800402
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700403 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800404 mLauncher.getDragController().completeAccessibleDrag(loc);
405
Sunny Goyal1a70cef2015-04-22 11:29:51 -0700406 if (!TextUtils.isEmpty(confirmation)) {
407 announceConfirmation(confirmation);
408 }
Adam Cohenc9735cf2015-01-23 16:11:55 -0800409 }
410
Sunny Goyala4647b62021-02-02 13:45:34 -0800411 private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
412 if (!itemSupportsAccessibleDrag(info)) {
413 return false;
414 }
415
Adam Cohenc9735cf2015-01-23 16:11:55 -0800416 mDragInfo = new DragInfo();
417 mDragInfo.info = info;
418 mDragInfo.item = item;
419 mDragInfo.dragType = DragType.ICON;
420 if (info instanceof FolderInfo) {
421 mDragInfo.dragType = DragType.FOLDER;
422 } else if (info instanceof LauncherAppWidgetInfo) {
423 mDragInfo.dragType = DragType.WIDGET;
424 }
425
Adam Cohenc9735cf2015-01-23 16:11:55 -0800426 Rect pos = new Rect();
427 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
Sunny Goyal94b510c2016-08-16 15:36:48 -0700428 mLauncher.getDragController().addDragListener(this);
429
430 DragOptions options = new DragOptions();
431 options.isAccessibleDrag = true;
Sunny Goyala4647b62021-02-02 13:45:34 -0800432 options.isKeyboardDrag = fromKeyboard;
Sunny Goyal9b180102020-03-11 10:02:29 -0700433 options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
Sunny Goyala4647b62021-02-02 13:45:34 -0800434
435 if (fromKeyboard) {
436 KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
437 .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
438 popup.showForIcon(item, info, options);
439 } else {
440 ItemLongClickListener.beginDrag(item, mLauncher, info, options);
441 }
442 return true;
Adam Cohenc9735cf2015-01-23 16:11:55 -0800443 }
444
Sunny Goyal45478022015-06-08 16:52:41 -0700445 @Override
Sunny Goyal94b510c2016-08-16 15:36:48 -0700446 public void onDragStart(DragObject dragObject, DragOptions options) {
Sunny Goyal45478022015-06-08 16:52:41 -0700447 // No-op
Adam Cohenc9735cf2015-01-23 16:11:55 -0800448 }
449
Sunny Goyal45478022015-06-08 16:52:41 -0700450 @Override
451 public void onDragEnd() {
452 mLauncher.getDragController().removeDragListener(this);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800453 mDragInfo = null;
Adam Cohenc9735cf2015-01-23 16:11:55 -0800454 }
Sunny Goyala9116722015-04-29 13:55:58 -0700455
456 /**
457 * Find empty space on the workspace and returns the screenId.
458 */
Sunny Goyalefb7e842018-10-04 15:11:00 -0700459 protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
Sunny Goyala9116722015-04-29 13:55:58 -0700460 Workspace workspace = mLauncher.getWorkspace();
Sunny Goyalefb7e842018-10-04 15:11:00 -0700461 IntArray workspaceScreens = workspace.getScreenOrder();
462 int screenId;
Sunny Goyala9116722015-04-29 13:55:58 -0700463
464 // First check if there is space on the current screen.
465 int screenIndex = workspace.getCurrentPage();
466 screenId = workspaceScreens.get(screenIndex);
467 CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex);
468
469 boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700470 screenIndex = 0;
Sunny Goyala9116722015-04-29 13:55:58 -0700471 while (!found && screenIndex < workspaceScreens.size()) {
472 screenId = workspaceScreens.get(screenIndex);
473 layout = (CellLayout) workspace.getPageAt(screenIndex);
474 found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
475 screenIndex++;
476 }
477
478 if (found) {
479 return screenId;
480 }
481
482 workspace.addExtraEmptyScreen();
483 screenId = workspace.commitExtraEmptyScreen();
484 layout = workspace.getScreenWithId(screenId);
485 found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
486
487 if (!found) {
488 Log.wtf(TAG, "Not enough space on an empty screen");
489 }
490 return screenId;
491 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700492
Sunny Goyal384b5782021-02-09 22:50:02 -0800493 public class LauncherAction {
494 public final int keyCode;
495 public final AccessibilityAction accessibilityAction;
496
497 private final LauncherAccessibilityDelegate mDelegate;
498
499 public LauncherAction(int id, int labelRes, int keyCode) {
500 this.keyCode = keyCode;
501 accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
502 mDelegate = LauncherAccessibilityDelegate.this;
503 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700504
505 /**
Sunny Goyal384b5782021-02-09 22:50:02 -0800506 * Invokes the action for the provided host
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700507 */
Sunny Goyal384b5782021-02-09 22:50:02 -0800508 public boolean invokeFromKeyboard(View host) {
509 if (host != null && host.getTag() instanceof ItemInfo) {
510 return mDelegate.performAction(
511 host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
512 } else {
513 return false;
514 }
515 }
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700516 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800517}