blob: 327fac46008b9140a3648c33b8567fd58d548e27 [file] [log] [blame]
Winson Chung97d85d22011-04-13 11:27:36 -07001/*
Hyunyoung Songee3e6a72015-02-20 14:25:27 -08002 * Copyright (C) 2015 The Android Open Source Project
Winson Chung97d85d22011-04-13 11:27:36 -07003 *
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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
Winson Chung97d85d22011-04-13 11:27:36 -070018
Winson Chung4e6a9762011-05-09 11:56:34 -070019import android.content.res.Configuration;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080020import android.util.Log;
Winson Chung97d85d22011-04-13 11:27:36 -070021import android.view.KeyEvent;
Sunny Goyalb3726d92014-08-20 16:58:17 -070022import android.view.SoundEffectConstants;
Winson Chung97d85d22011-04-13 11:27:36 -070023import android.view.View;
24import android.view.ViewGroup;
Winson Chung97d85d22011-04-13 11:27:36 -070025
Sunny Goyal290800b2015-03-05 11:33:33 -080026import com.android.launcher3.FocusHelper.PagedViewKeyListener;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080027import com.android.launcher3.util.FocusLogic;
Adam Cohen091440a2015-03-18 14:16:05 -070028import com.android.launcher3.util.Thunk;
Winson Chungfaa13252011-06-13 18:15:54 -070029
Winson Chung97d85d22011-04-13 11:27:36 -070030/**
Winson Chung4d279d92011-07-21 11:46:32 -070031 * A keyboard listener we set on all the workspace icons.
32 */
Adam Cohenac56cff2011-09-28 20:45:37 -070033class IconKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080034 @Override
Winson Chung4d279d92011-07-21 11:46:32 -070035 public boolean onKey(View v, int keyCode, KeyEvent event) {
Adam Cohenac56cff2011-09-28 20:45:37 -070036 return FocusHelper.handleIconKeyEvent(v, keyCode, event);
37 }
38}
39
40/**
41 * A keyboard listener we set on all the workspace icons.
42 */
43class FolderKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080044 @Override
Adam Cohenac56cff2011-09-28 20:45:37 -070045 public boolean onKey(View v, int keyCode, KeyEvent event) {
46 return FocusHelper.handleFolderKeyEvent(v, keyCode, event);
Winson Chung4d279d92011-07-21 11:46:32 -070047 }
48}
49
50/**
Winson Chung3d503fb2011-07-13 17:25:49 -070051 * A keyboard listener we set on all the hotseat buttons.
Winson Chung4e6a9762011-05-09 11:56:34 -070052 */
Adam Cohenac56cff2011-09-28 20:45:37 -070053class HotseatIconKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080054 @Override
Winson Chung4e6a9762011-05-09 11:56:34 -070055 public boolean onKey(View v, int keyCode, KeyEvent event) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080056 return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
Winson Chung4e6a9762011-05-09 11:56:34 -070057 }
58}
59
Winson Chung97d85d22011-04-13 11:27:36 -070060public class FocusHelper {
Winson Chung97d85d22011-04-13 11:27:36 -070061
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080062 private static final String TAG = "FocusHelper";
63 private static final boolean DEBUG = false;
64
65 //
66 // Key code handling methods.
67 //
68
69 /**
Sunny Goyal290800b2015-03-05 11:33:33 -080070 * A keyboard listener for scrollable folders
71 */
72 public static class PagedFolderKeyEventListener extends PagedViewKeyListener {
73
74 private final Folder mFolder;
75
76 public PagedFolderKeyEventListener(Folder folder) {
77 mFolder = folder;
78 }
79
80 @Override
81 public void handleNoopKey(int keyCode, View v) {
82 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
83 mFolder.mFolderName.requestFocus();
84 playSoundEffect(keyCode, v);
85 }
86 }
87 }
88
89 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080090 * Handles key events in the all apps screen.
91 */
Sunny Goyal290800b2015-03-05 11:33:33 -080092 public static class PagedViewKeyListener implements View.OnKeyListener {
93
94 @Override
95 public boolean onKey(View v, int keyCode, KeyEvent e) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080096 boolean consume = FocusLogic.shouldConsume(keyCode);
97 if (e.getAction() == KeyEvent.ACTION_UP) {
98 return consume;
99 }
100 if (DEBUG) {
101 Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].",
102 KeyEvent.keyCodeToString(keyCode)));
103 }
104
105 // Initialize variables.
106 ViewGroup parentLayout;
107 ViewGroup itemContainer;
108 int countX;
109 int countY;
110 if (v.getParent() instanceof ShortcutAndWidgetContainer) {
111 itemContainer = (ViewGroup) v.getParent();
112 parentLayout = (ViewGroup) itemContainer.getParent();
113 countX = ((CellLayout) parentLayout).getCountX();
114 countY = ((CellLayout) parentLayout).getCountY();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800115 } else {
Sunny Goyal290800b2015-03-05 11:33:33 -0800116 if (LauncherAppState.isDogfoodBuild()) {
117 throw new IllegalStateException("Parent of the focused item is not supported.");
118 } else {
119 return false;
120 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800121 }
Sunny Goyal290800b2015-03-05 11:33:33 -0800122
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800123 final int iconIndex = itemContainer.indexOfChild(v);
124 final PagedView container = (PagedView) parentLayout.getParent();
125 final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
126 final int pageCount = container.getChildCount();
127 ViewGroup newParent = null;
128 View child = null;
Hyunyoung Song38531712015-03-03 19:25:16 -0800129 // TODO(hyunyoungs): this matrix is not applicable on the last page.
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800130 int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
131
132 // Process focus.
133 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
134 iconIndex, pageIndex, pageCount);
135 if (newIconIndex == FocusLogic.NOOP) {
Sunny Goyal290800b2015-03-05 11:33:33 -0800136 handleNoopKey(keyCode, v);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800137 return consume;
138 }
139 switch (newIconIndex) {
Hyunyoung Song38531712015-03-03 19:25:16 -0800140 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
141 newParent = getAppsCustomizePage(container, pageIndex -1);
142 if (newParent != null) {
143 int row = FocusLogic.findRow(matrix, iconIndex);
144 container.snapToPage(pageIndex - 1);
145 // no need to create a new matrix.
146 child = newParent.getChildAt(matrix[countX-1][row]);
147 }
148 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800149 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
150 newParent = getAppsCustomizePage(container, pageIndex - 1);
151 if (newParent != null) {
152 container.snapToPage(pageIndex - 1);
153 child = newParent.getChildAt(0);
154 }
Hyunyoung Song38531712015-03-03 19:25:16 -0800155 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800156 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
157 newParent = getAppsCustomizePage(container, pageIndex - 1);
158 if (newParent != null) {
159 container.snapToPage(pageIndex - 1);
160 child = newParent.getChildAt(newParent.getChildCount() - 1);
161 }
162 break;
163 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
164 newParent = getAppsCustomizePage(container, pageIndex + 1);
165 if (newParent != null) {
166 container.snapToPage(pageIndex + 1);
167 child = newParent.getChildAt(0);
168 }
169 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800170 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
171 newParent = getAppsCustomizePage(container, pageIndex + 1);
172 if (newParent != null) {
173 container.snapToPage(pageIndex + 1);
174 int row = FocusLogic.findRow(matrix, iconIndex);
175 child = newParent.getChildAt(matrix[0][row]);
176 }
177 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800178 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
179 child = container.getChildAt(0);
180 break;
181 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
182 child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
183 break;
184 default: // Go to some item on the current page.
185 child = itemContainer.getChildAt(newIconIndex);
186 break;
187 }
188 if (child != null) {
189 child.requestFocus();
190 playSoundEffect(keyCode, v);
Sunny Goyal290800b2015-03-05 11:33:33 -0800191 } else {
192 handleNoopKey(keyCode, v);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800193 }
194 return consume;
195 }
196
Sunny Goyal290800b2015-03-05 11:33:33 -0800197 public void handleNoopKey(int keyCode, View v) { }
198 }
199
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800200 /**
201 * Handles key events in the workspace hot seat (bottom of the screen).
202 * <p>Currently we don't special case for the phone UI in different orientations, even though
203 * the hotseat is on the side in landscape mode. This is to ensure that accessibility
204 * consistency is maintained across rotations.
205 */
206 static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
207 boolean consume = FocusLogic.shouldConsume(keyCode);
208 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
209 return consume;
210 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800211
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700212 LauncherAppState app = LauncherAppState.getInstance();
213 DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800214 if (DEBUG) {
215 Log.v(TAG, String.format(
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700216 "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
217 KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800218 }
219
220 // Initialize the variables.
221 final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
222 final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800223 Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
224
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800225 Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
226 int pageIndex = workspace.getCurrentPage();
227 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800228 int countX = -1;
229 int countY = -1;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800230 int iconIndex = findIndexOfView(hotseatParent, v);
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700231 int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
232 .getChildAt(iconIndex).getLayoutParams()).cellX;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800233
234 final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
235 final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
236
237 ViewGroup parent = null;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800238 int[][] matrix = null;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800239
240 if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700241 !profile.isVerticalBarLayout()) {
242 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
243 true /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
244 iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800245 iconIndex += iconParent.getChildCount();
246 countX = iconLayout.getCountX();
247 countY = iconLayout.getCountY() + hotseatLayout.getCountY();
248 parent = iconParent;
249 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700250 profile.isVerticalBarLayout()) {
251 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
252 false /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
253 iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800254 iconIndex += iconParent.getChildCount();
255 countX = iconLayout.getCountX() + hotseatLayout.getCountX();
256 countY = iconLayout.getCountY();
257 parent = iconParent;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800258 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700259 profile.isVerticalBarLayout()) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800260 keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
261 }else {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800262 // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
263 // matrix extended with hotseat.
264 matrix = FocusLogic.createSparseMatrix(hotseatLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800265 countX = hotseatLayout.getCountX();
266 countY = hotseatLayout.getCountY();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800267 parent = hotseatParent;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800268 }
269
270 // Process the focus.
271 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
272 iconIndex, pageIndex, pageCount);
273
Hyunyoung Song31178b82015-02-24 14:12:51 -0800274 View newIcon = null;
275 if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
276 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
277 newIcon = parent.getChildAt(0);
278 // TODO(hyunyoungs): handle cases where the child is not an icon but
279 // a folder or a widget.
280 workspace.snapToPage(pageIndex + 1);
281 }
282 if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800283 newIconIndex -= iconParent.getChildCount();
284 }
285 if (parent != null) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800286 if (newIcon == null && newIconIndex >=0) {
287 newIcon = parent.getChildAt(newIconIndex);
288 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800289 if (newIcon != null) {
290 newIcon.requestFocus();
291 playSoundEffect(keyCode, v);
292 }
293 }
294 return consume;
295 }
296
297 /**
298 * Handles key events in a workspace containing icons.
299 */
300 static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
301 boolean consume = FocusLogic.shouldConsume(keyCode);
302 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
303 return consume;
304 }
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700305
306 LauncherAppState app = LauncherAppState.getInstance();
307 DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
308
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800309 if (DEBUG) {
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700310 Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
311 KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800312 }
313
314 // Initialize the variables.
315 ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
Hyunyoung Song38531712015-03-03 19:25:16 -0800316 CellLayout iconLayout = (CellLayout) parent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800317 final Workspace workspace = (Workspace) iconLayout.getParent();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800318 final ViewGroup launcher = (ViewGroup) workspace.getParent();
319 final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800320 final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
321 int pageIndex = workspace.indexOfChild(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800322 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800323 int countX = iconLayout.getCountX();
324 int countY = iconLayout.getCountY();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800325 final int iconIndex = findIndexOfView(parent, v);
326
327 CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
328 ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
329 int[][] matrix;
330
Hyunyoung Song31178b82015-02-24 14:12:51 -0800331 // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800332 // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
333 // with the hotseat.
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700334 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
335 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
Hyunyoung Song31178b82015-02-24 14:12:51 -0800336 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800337 countY = countY + 1;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800338 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700339 profile.isVerticalBarLayout()) {
340 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
Hyunyoung Song31178b82015-02-24 14:12:51 -0800341 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
342 countX = countX + 1;
343 } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
344 workspace.removeWorkspaceItem(v);
345 return consume;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800346 } else {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800347 matrix = FocusLogic.createSparseMatrix(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800348 }
349
350 // Process the focus.
351 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
352 iconIndex, pageIndex, pageCount);
353 View newIcon = null;
354 switch (newIconIndex) {
355 case FocusLogic.NOOP:
356 if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
357 newIcon = tabs;
358 }
359 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800360 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
361 int row = FocusLogic.findRow(matrix, iconIndex);
362 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
363 if (parent != null) {
364 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800365 matrix = FocusLogic.createSparseMatrix(iconLayout,
Hyunyoung Song38531712015-03-03 19:25:16 -0800366 iconLayout.getCountX(), row);
367 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
368 FocusLogic.PIVOT, pageIndex - 1, pageCount);
369 newIcon = parent.getChildAt(newIconIndex);
370 }
371 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800372 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
373 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
374 newIcon = parent.getChildAt(0);
375 workspace.snapToPage(pageIndex - 1);
Hyunyoung Song38531712015-03-03 19:25:16 -0800376 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800377 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
378 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
379 newIcon = parent.getChildAt(parent.getChildCount() - 1);
380 workspace.snapToPage(pageIndex - 1);
381 break;
382 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
383 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
384 newIcon = parent.getChildAt(0);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800385 workspace.snapToPage(pageIndex + 1);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800386 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800387 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
388 row = FocusLogic.findRow(matrix, iconIndex);
389 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
390 if (parent != null) {
391 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800392 matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
Hyunyoung Song38531712015-03-03 19:25:16 -0800393 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
394 FocusLogic.PIVOT, pageIndex, pageCount);
395 newIcon = parent.getChildAt(newIconIndex);
396 }
397 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800398 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
399 newIcon = parent.getChildAt(0);
400 break;
401 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
402 newIcon = parent.getChildAt(parent.getChildCount() - 1);
403 break;
404 default:
405 // current page, some item.
406 if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
407 newIcon = parent.getChildAt(newIconIndex);
408 } else if (parent.getChildCount() <= newIconIndex &&
409 newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
410 newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
411 }
412 break;
413 }
414 if (newIcon != null) {
415 newIcon.requestFocus();
416 playSoundEffect(keyCode, v);
417 }
418 return consume;
419 }
420
421 /**
422 * Handles key events for items in a Folder.
423 */
424 static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
425 boolean consume = FocusLogic.shouldConsume(keyCode);
426 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
427 return consume;
428 }
429 if (DEBUG) {
430 Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].",
431 KeyEvent.keyCodeToString(keyCode)));
432 }
433
434 // Initialize the variables.
435 ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
436 final CellLayout layout = (CellLayout) parent.getParent();
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800437 final Folder folder = (Folder) layout.getParent().getParent();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800438 View title = folder.mFolderName;
439 Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
440 final int countX = layout.getCountX();
441 final int countY = layout.getCountY();
442 final int iconIndex = findIndexOfView(parent, v);
443 int pageIndex = workspace.indexOfChild(layout);
444 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800445 int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800446
447 // Process the focus.
448 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex,
449 pageIndex, pageCount);
450 View newIcon = null;
451 switch (newIconIndex) {
452 case FocusLogic.NOOP:
453 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
454 newIcon = title;
455 }
456 break;
457 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
458 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
459 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
460 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
461 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
462 if (DEBUG) {
463 Log.v(TAG, "Page advance handling not supported on folder icons.");
464 }
465 break;
466 default: // current page some item.
467 newIcon = parent.getChildAt(newIconIndex);
468 break;
469 }
470 if (newIcon != null) {
471 newIcon.requestFocus();
472 playSoundEffect(keyCode, v);
473 }
474 return consume;
475 }
476
477 //
478 // Helper methods.
479 //
480
Winson Chung97d85d22011-04-13 11:27:36 -0700481 /**
Adam Cohenae4f1552011-10-20 00:15:42 -0700482 * Returns the Viewgroup containing page contents for the page at the index specified.
483 */
Adam Cohen091440a2015-03-18 14:16:05 -0700484 @Thunk static ViewGroup getAppsCustomizePage(ViewGroup container, int index) {
Adam Cohenae4f1552011-10-20 00:15:42 -0700485 ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index);
Winson Chungc58497e2013-09-03 17:48:37 -0700486 if (page instanceof CellLayout) {
Adam Cohenae4f1552011-10-20 00:15:42 -0700487 // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren
Winson Chungc58497e2013-09-03 17:48:37 -0700488 page = ((CellLayout) page).getShortcutsAndWidgets();
Adam Cohenae4f1552011-10-20 00:15:42 -0700489 }
490 return page;
491 }
492
493 /**
Winson Chung97d85d22011-04-13 11:27:36 -0700494 * Private helper method to get the CellLayoutChildren given a CellLayout index.
495 */
Michael Jurkaa52570f2012-03-20 03:18:20 -0700496 private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
497 ViewGroup container, int i) {
Sunny Goyalb3726d92014-08-20 16:58:17 -0700498 CellLayout parent = (CellLayout) container.getChildAt(i);
499 return parent.getShortcutsAndWidgets();
Winson Chung97d85d22011-04-13 11:27:36 -0700500 }
501
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800502 private static int findIndexOfView(ViewGroup parent, View v) {
503 for (int i = 0; i < parent.getChildCount(); i++) {
504 if (v != null && v.equals(parent.getChildAt(i))) {
505 return i;
Winson Chung97d85d22011-04-13 11:27:36 -0700506 }
507 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800508 return -1;
Winson Chung97d85d22011-04-13 11:27:36 -0700509 }
510
511 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800512 * Helper method to be used for playing sound effects.
Winson Chung97d85d22011-04-13 11:27:36 -0700513 */
Adam Cohen091440a2015-03-18 14:16:05 -0700514 @Thunk static void playSoundEffect(int keyCode, View v) {
Winson Chung97d85d22011-04-13 11:27:36 -0700515 switch (keyCode) {
516 case KeyEvent.KEYCODE_DPAD_LEFT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800517 v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
Winson Chung97d85d22011-04-13 11:27:36 -0700518 break;
519 case KeyEvent.KEYCODE_DPAD_RIGHT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800520 v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
Winson Chung97d85d22011-04-13 11:27:36 -0700521 break;
522 case KeyEvent.KEYCODE_DPAD_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700523 case KeyEvent.KEYCODE_PAGE_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700524 case KeyEvent.KEYCODE_MOVE_END:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800525 v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
Adam Cohenac56cff2011-09-28 20:45:37 -0700526 break;
527 case KeyEvent.KEYCODE_DPAD_UP:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800528 case KeyEvent.KEYCODE_PAGE_UP:
Adam Cohenac56cff2011-09-28 20:45:37 -0700529 case KeyEvent.KEYCODE_MOVE_HOME:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800530 v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
Adam Cohenac56cff2011-09-28 20:45:37 -0700531 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800532 default:
Winson Chung97d85d22011-04-13 11:27:36 -0700533 break;
Winson Chung97d85d22011-04-13 11:27:36 -0700534 }
Winson Chung97d85d22011-04-13 11:27:36 -0700535 }
536}