blob: 32ed98c17b48ad561279333bad6da128ece6e6d5 [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/**
Winson Chung3d503fb2011-07-13 17:25:49 -070041 * A keyboard listener we set on all the hotseat buttons.
Winson Chung4e6a9762011-05-09 11:56:34 -070042 */
Adam Cohenac56cff2011-09-28 20:45:37 -070043class HotseatIconKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080044 @Override
Winson Chung4e6a9762011-05-09 11:56:34 -070045 public boolean onKey(View v, int keyCode, KeyEvent event) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080046 return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
Winson Chung4e6a9762011-05-09 11:56:34 -070047 }
48}
49
Winson Chung97d85d22011-04-13 11:27:36 -070050public class FocusHelper {
Winson Chung97d85d22011-04-13 11:27:36 -070051
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080052 private static final String TAG = "FocusHelper";
53 private static final boolean DEBUG = false;
54
55 //
56 // Key code handling methods.
57 //
58
59 /**
Sunny Goyal290800b2015-03-05 11:33:33 -080060 * A keyboard listener for scrollable folders
61 */
62 public static class PagedFolderKeyEventListener extends PagedViewKeyListener {
63
64 private final Folder mFolder;
65
66 public PagedFolderKeyEventListener(Folder folder) {
67 mFolder = folder;
68 }
69
70 @Override
71 public void handleNoopKey(int keyCode, View v) {
72 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
73 mFolder.mFolderName.requestFocus();
74 playSoundEffect(keyCode, v);
75 }
76 }
77 }
78
79 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080080 * Handles key events in the all apps screen.
81 */
Sunny Goyal290800b2015-03-05 11:33:33 -080082 public static class PagedViewKeyListener implements View.OnKeyListener {
83
Hyunyoung Songada50982015-04-10 14:35:23 -070084 @Override
85 public boolean onKey(View v, int keyCode, KeyEvent e) {
86 boolean consume = FocusLogic.shouldConsume(keyCode);
87 if (e.getAction() == KeyEvent.ACTION_UP) {
88 return consume;
Sunny Goyal290800b2015-03-05 11:33:33 -080089 }
Hyunyoung Songada50982015-04-10 14:35:23 -070090 if (DEBUG) {
91 Log.v(TAG, String.format("Handle ALL APPS and Folders keyevent=[%s].",
92 KeyEvent.keyCodeToString(keyCode)));
93 }
Sunny Goyal290800b2015-03-05 11:33:33 -080094
Hyunyoung Songada50982015-04-10 14:35:23 -070095 // Initialize variables.
96 ViewGroup parentLayout;
97 ViewGroup itemContainer;
98 int countX;
99 int countY;
100 if (v.getParent() instanceof ShortcutAndWidgetContainer) {
101 itemContainer = (ViewGroup) v.getParent();
102 parentLayout = (ViewGroup) itemContainer.getParent();
103 countX = ((CellLayout) parentLayout).getCountX();
104 countY = ((CellLayout) parentLayout).getCountY();
105 } else {
106 if (LauncherAppState.isDogfoodBuild()) {
107 throw new IllegalStateException("Parent of the focused item is not supported.");
108 } else {
109 return false;
110 }
111 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800112
Hyunyoung Songada50982015-04-10 14:35:23 -0700113 final int iconIndex = itemContainer.indexOfChild(v);
114 final PagedView container = (PagedView) parentLayout.getParent();
115 final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
116 final int pageCount = container.getChildCount();
117 ViewGroup newParent = null;
118 View child = null;
119 // TODO(hyunyoungs): this matrix is not applicable on the last page.
120 int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
121
122 // Process focus.
123 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
124 iconIndex, pageIndex, pageCount);
125 if (newIconIndex == FocusLogic.NOOP) {
126 handleNoopKey(keyCode, v);
127 return consume;
128 }
129 switch (newIconIndex) {
130 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
131 case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
132 int newPageIndex = pageIndex - 1;
133 if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
134 newPageIndex = pageIndex + 1;
135 }
136 newParent = getAppsCustomizePage(container, newPageIndex);
137 if (newParent != null) {
138 int row = FocusLogic.findRow(matrix, iconIndex);
139 container.snapToPage(newPageIndex);
140 // no need to create a new matrix.
141 child = newParent.getChildAt(matrix[countX-1][row]);
142 }
143 break;
144 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
145 newParent = getAppsCustomizePage(container, pageIndex - 1);
146 if (newParent != null) {
147 container.snapToPage(pageIndex - 1);
148 child = newParent.getChildAt(0);
149 }
150 break;
151 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
152 newParent = getAppsCustomizePage(container, pageIndex - 1);
153 if (newParent != null) {
154 container.snapToPage(pageIndex - 1);
155 child = newParent.getChildAt(newParent.getChildCount() - 1);
156 }
157 break;
158 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
159 newParent = getAppsCustomizePage(container, pageIndex + 1);
160 if (newParent != null) {
161 container.snapToPage(pageIndex + 1);
162 child = newParent.getChildAt(0);
163 }
164 break;
165 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
166 case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
167 newPageIndex = pageIndex + 1;
168 if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
169 newPageIndex = pageIndex -1;
170 }
171 newParent = getAppsCustomizePage(container, newPageIndex);
172 if (newParent != null) {
173 container.snapToPage(newPageIndex);
174 int row = FocusLogic.findRow(matrix, iconIndex);
175 child = newParent.getChildAt(matrix[0][row]);
176 }
177 break;
178 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);
191 } else {
192 handleNoopKey(keyCode, v);
193 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800194 return consume;
195 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800196
Hyunyoung Songada50982015-04-10 14:35:23 -0700197 public void handleNoopKey(int keyCode, View v) { }
Sunny Goyal290800b2015-03-05 11:33:33 -0800198 }
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 Songada50982015-04-10 14:35:23 -0700212 DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800213 if (DEBUG) {
214 Log.v(TAG, String.format(
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700215 "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
216 KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800217 }
218
219 // Initialize the variables.
220 final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
221 final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800222 Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
223
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800224 Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
Hyunyoung Songb76cd622015-04-16 14:34:09 -0700225 int pageIndex = workspace.getNextPage();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800226 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800227 int countX = -1;
228 int countY = -1;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800229 int iconIndex = findIndexOfView(hotseatParent, v);
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700230 int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
231 .getChildAt(iconIndex).getLayoutParams()).cellX;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800232
233 final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
Hyunyoung Songb76cd622015-04-16 14:34:09 -0700234 if (iconLayout == null) {
235 // This check is to guard against cases where key strokes rushes in when workspace
236 // child creation/deletion is still in flux. (e.g., during drop or fling
237 // animation.)
238 return consume;
239 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800240 final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
241
242 ViewGroup parent = null;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800243 int[][] matrix = null;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800244
245 if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700246 !profile.isVerticalBarLayout()) {
247 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
248 true /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
249 iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800250 iconIndex += iconParent.getChildCount();
251 countX = iconLayout.getCountX();
252 countY = iconLayout.getCountY() + hotseatLayout.getCountY();
253 parent = iconParent;
254 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700255 profile.isVerticalBarLayout()) {
256 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
257 false /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
258 iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800259 iconIndex += iconParent.getChildCount();
260 countX = iconLayout.getCountX() + hotseatLayout.getCountX();
261 countY = iconLayout.getCountY();
262 parent = iconParent;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800263 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700264 profile.isVerticalBarLayout()) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800265 keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
266 }else {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800267 // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
268 // matrix extended with hotseat.
269 matrix = FocusLogic.createSparseMatrix(hotseatLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800270 countX = hotseatLayout.getCountX();
271 countY = hotseatLayout.getCountY();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800272 parent = hotseatParent;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800273 }
274
275 // Process the focus.
276 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
277 iconIndex, pageIndex, pageCount);
278
Hyunyoung Song31178b82015-02-24 14:12:51 -0800279 View newIcon = null;
280 if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
281 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
282 newIcon = parent.getChildAt(0);
283 // TODO(hyunyoungs): handle cases where the child is not an icon but
284 // a folder or a widget.
285 workspace.snapToPage(pageIndex + 1);
286 }
287 if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800288 newIconIndex -= iconParent.getChildCount();
289 }
290 if (parent != null) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800291 if (newIcon == null && newIconIndex >=0) {
292 newIcon = parent.getChildAt(newIconIndex);
293 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800294 if (newIcon != null) {
295 newIcon.requestFocus();
296 playSoundEffect(keyCode, v);
297 }
298 }
299 return consume;
300 }
301
302 /**
303 * Handles key events in a workspace containing icons.
304 */
305 static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
306 boolean consume = FocusLogic.shouldConsume(keyCode);
307 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
308 return consume;
309 }
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700310
311 LauncherAppState app = LauncherAppState.getInstance();
312 DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
313
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800314 if (DEBUG) {
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700315 Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
316 KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800317 }
318
319 // Initialize the variables.
320 ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
Hyunyoung Song38531712015-03-03 19:25:16 -0800321 CellLayout iconLayout = (CellLayout) parent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800322 final Workspace workspace = (Workspace) iconLayout.getParent();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800323 final ViewGroup launcher = (ViewGroup) workspace.getParent();
324 final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800325 final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
326 int pageIndex = workspace.indexOfChild(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800327 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800328 int countX = iconLayout.getCountX();
329 int countY = iconLayout.getCountY();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800330 final int iconIndex = findIndexOfView(parent, v);
331
332 CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
333 ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
334 int[][] matrix;
335
Hyunyoung Song31178b82015-02-24 14:12:51 -0800336 // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800337 // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
338 // with the hotseat.
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700339 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
340 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
Hyunyoung Song31178b82015-02-24 14:12:51 -0800341 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800342 countY = countY + 1;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800343 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
Hyunyoung Song18bfaaf2015-03-17 11:32:21 -0700344 profile.isVerticalBarLayout()) {
345 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
Hyunyoung Song31178b82015-02-24 14:12:51 -0800346 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
347 countX = countX + 1;
348 } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
349 workspace.removeWorkspaceItem(v);
350 return consume;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800351 } else {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800352 matrix = FocusLogic.createSparseMatrix(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800353 }
354
355 // Process the focus.
356 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
357 iconIndex, pageIndex, pageCount);
358 View newIcon = null;
359 switch (newIconIndex) {
360 case FocusLogic.NOOP:
361 if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
362 newIcon = tabs;
363 }
364 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800365 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
Hyunyoung Songada50982015-04-10 14:35:23 -0700366 case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
367 int newPageIndex = pageIndex - 1;
368 if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
369 newPageIndex = pageIndex + 1;
370 }
Hyunyoung Song38531712015-03-03 19:25:16 -0800371 int row = FocusLogic.findRow(matrix, iconIndex);
Hyunyoung Songada50982015-04-10 14:35:23 -0700372 parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
Hyunyoung Songb76cd622015-04-16 14:34:09 -0700373 workspace.snapToPage(newPageIndex);
Hyunyoung Song38531712015-03-03 19:25:16 -0800374 if (parent != null) {
375 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800376 matrix = FocusLogic.createSparseMatrix(iconLayout,
Hyunyoung Song38531712015-03-03 19:25:16 -0800377 iconLayout.getCountX(), row);
378 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
Hyunyoung Songada50982015-04-10 14:35:23 -0700379 FocusLogic.PIVOT, newPageIndex, pageCount);
Hyunyoung Song38531712015-03-03 19:25:16 -0800380 newIcon = parent.getChildAt(newIconIndex);
381 }
382 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800383 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
384 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
385 newIcon = parent.getChildAt(0);
386 workspace.snapToPage(pageIndex - 1);
Hyunyoung Song38531712015-03-03 19:25:16 -0800387 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800388 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
389 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
390 newIcon = parent.getChildAt(parent.getChildCount() - 1);
391 workspace.snapToPage(pageIndex - 1);
392 break;
393 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
394 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
395 newIcon = parent.getChildAt(0);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800396 workspace.snapToPage(pageIndex + 1);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800397 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800398 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
Hyunyoung Songada50982015-04-10 14:35:23 -0700399 case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
400 newPageIndex = pageIndex + 1;
401 if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
402 newPageIndex = pageIndex - 1;
403 }
Hyunyoung Songb76cd622015-04-16 14:34:09 -0700404 workspace.snapToPage(newPageIndex);
Hyunyoung Song38531712015-03-03 19:25:16 -0800405 row = FocusLogic.findRow(matrix, iconIndex);
Hyunyoung Songada50982015-04-10 14:35:23 -0700406 parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
Hyunyoung Song38531712015-03-03 19:25:16 -0800407 if (parent != null) {
408 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800409 matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
Hyunyoung Song38531712015-03-03 19:25:16 -0800410 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
Hyunyoung Songada50982015-04-10 14:35:23 -0700411 FocusLogic.PIVOT, newPageIndex, pageCount);
Hyunyoung Song38531712015-03-03 19:25:16 -0800412 newIcon = parent.getChildAt(newIconIndex);
413 }
414 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800415 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
416 newIcon = parent.getChildAt(0);
417 break;
418 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
419 newIcon = parent.getChildAt(parent.getChildCount() - 1);
420 break;
421 default:
422 // current page, some item.
423 if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
424 newIcon = parent.getChildAt(newIconIndex);
425 } else if (parent.getChildCount() <= newIconIndex &&
426 newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
427 newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
428 }
429 break;
430 }
431 if (newIcon != null) {
432 newIcon.requestFocus();
433 playSoundEffect(keyCode, v);
434 }
435 return consume;
436 }
437
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800438 //
439 // Helper methods.
440 //
441
Winson Chung97d85d22011-04-13 11:27:36 -0700442 /**
Adam Cohenae4f1552011-10-20 00:15:42 -0700443 * Returns the Viewgroup containing page contents for the page at the index specified.
444 */
Adam Cohen091440a2015-03-18 14:16:05 -0700445 @Thunk static ViewGroup getAppsCustomizePage(ViewGroup container, int index) {
Adam Cohenae4f1552011-10-20 00:15:42 -0700446 ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index);
Winson Chungc58497e2013-09-03 17:48:37 -0700447 if (page instanceof CellLayout) {
Adam Cohenae4f1552011-10-20 00:15:42 -0700448 // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren
Winson Chungc58497e2013-09-03 17:48:37 -0700449 page = ((CellLayout) page).getShortcutsAndWidgets();
Adam Cohenae4f1552011-10-20 00:15:42 -0700450 }
451 return page;
452 }
453
454 /**
Winson Chung97d85d22011-04-13 11:27:36 -0700455 * Private helper method to get the CellLayoutChildren given a CellLayout index.
456 */
Michael Jurkaa52570f2012-03-20 03:18:20 -0700457 private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
458 ViewGroup container, int i) {
Sunny Goyalb3726d92014-08-20 16:58:17 -0700459 CellLayout parent = (CellLayout) container.getChildAt(i);
460 return parent.getShortcutsAndWidgets();
Winson Chung97d85d22011-04-13 11:27:36 -0700461 }
462
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800463 private static int findIndexOfView(ViewGroup parent, View v) {
464 for (int i = 0; i < parent.getChildCount(); i++) {
465 if (v != null && v.equals(parent.getChildAt(i))) {
466 return i;
Winson Chung97d85d22011-04-13 11:27:36 -0700467 }
468 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800469 return -1;
Winson Chung97d85d22011-04-13 11:27:36 -0700470 }
471
472 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800473 * Helper method to be used for playing sound effects.
Winson Chung97d85d22011-04-13 11:27:36 -0700474 */
Adam Cohen091440a2015-03-18 14:16:05 -0700475 @Thunk static void playSoundEffect(int keyCode, View v) {
Winson Chung97d85d22011-04-13 11:27:36 -0700476 switch (keyCode) {
477 case KeyEvent.KEYCODE_DPAD_LEFT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800478 v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
Winson Chung97d85d22011-04-13 11:27:36 -0700479 break;
480 case KeyEvent.KEYCODE_DPAD_RIGHT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800481 v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
Winson Chung97d85d22011-04-13 11:27:36 -0700482 break;
483 case KeyEvent.KEYCODE_DPAD_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700484 case KeyEvent.KEYCODE_PAGE_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700485 case KeyEvent.KEYCODE_MOVE_END:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800486 v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
Adam Cohenac56cff2011-09-28 20:45:37 -0700487 break;
488 case KeyEvent.KEYCODE_DPAD_UP:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800489 case KeyEvent.KEYCODE_PAGE_UP:
Adam Cohenac56cff2011-09-28 20:45:37 -0700490 case KeyEvent.KEYCODE_MOVE_HOME:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800491 v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
Adam Cohenac56cff2011-09-28 20:45:37 -0700492 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800493 default:
Winson Chung97d85d22011-04-13 11:27:36 -0700494 break;
Winson Chung97d85d22011-04-13 11:27:36 -0700495 }
Winson Chung97d85d22011-04-13 11:27:36 -0700496 }
497}