blob: b090a7c3f1fc1b6a5f37a1dd47ed62ad59308ccc [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;
Winson Chungfaa13252011-06-13 18:15:54 -070028
Winson Chung97d85d22011-04-13 11:27:36 -070029/**
Winson Chung4d279d92011-07-21 11:46:32 -070030 * A keyboard listener we set on all the workspace icons.
31 */
Adam Cohenac56cff2011-09-28 20:45:37 -070032class IconKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080033 @Override
Winson Chung4d279d92011-07-21 11:46:32 -070034 public boolean onKey(View v, int keyCode, KeyEvent event) {
Adam Cohenac56cff2011-09-28 20:45:37 -070035 return FocusHelper.handleIconKeyEvent(v, keyCode, event);
36 }
37}
38
39/**
40 * A keyboard listener we set on all the workspace icons.
41 */
42class FolderKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080043 @Override
Adam Cohenac56cff2011-09-28 20:45:37 -070044 public boolean onKey(View v, int keyCode, KeyEvent event) {
45 return FocusHelper.handleFolderKeyEvent(v, keyCode, event);
Winson Chung4d279d92011-07-21 11:46:32 -070046 }
47}
48
49/**
Winson Chung3d503fb2011-07-13 17:25:49 -070050 * A keyboard listener we set on all the hotseat buttons.
Winson Chung4e6a9762011-05-09 11:56:34 -070051 */
Adam Cohenac56cff2011-09-28 20:45:37 -070052class HotseatIconKeyEventListener implements View.OnKeyListener {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080053 @Override
Winson Chung4e6a9762011-05-09 11:56:34 -070054 public boolean onKey(View v, int keyCode, KeyEvent event) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080055 return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
Winson Chung4e6a9762011-05-09 11:56:34 -070056 }
57}
58
Winson Chung97d85d22011-04-13 11:27:36 -070059public class FocusHelper {
Winson Chung97d85d22011-04-13 11:27:36 -070060
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080061 private static final String TAG = "FocusHelper";
62 private static final boolean DEBUG = false;
63
64 //
65 // Key code handling methods.
66 //
67
68 /**
Sunny Goyal290800b2015-03-05 11:33:33 -080069 * A keyboard listener for scrollable folders
70 */
71 public static class PagedFolderKeyEventListener extends PagedViewKeyListener {
72
73 private final Folder mFolder;
74
75 public PagedFolderKeyEventListener(Folder folder) {
76 mFolder = folder;
77 }
78
79 @Override
80 public void handleNoopKey(int keyCode, View v) {
81 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
82 mFolder.mFolderName.requestFocus();
83 playSoundEffect(keyCode, v);
84 }
85 }
86 }
87
88 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080089 * Handles key events in the all apps screen.
90 */
Sunny Goyal290800b2015-03-05 11:33:33 -080091 public static class PagedViewKeyListener implements View.OnKeyListener {
92
93 @Override
94 public boolean onKey(View v, int keyCode, KeyEvent e) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -080095 boolean consume = FocusLogic.shouldConsume(keyCode);
96 if (e.getAction() == KeyEvent.ACTION_UP) {
97 return consume;
98 }
99 if (DEBUG) {
100 Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].",
101 KeyEvent.keyCodeToString(keyCode)));
102 }
103
104 // Initialize variables.
105 ViewGroup parentLayout;
106 ViewGroup itemContainer;
107 int countX;
108 int countY;
109 if (v.getParent() instanceof ShortcutAndWidgetContainer) {
110 itemContainer = (ViewGroup) v.getParent();
111 parentLayout = (ViewGroup) itemContainer.getParent();
112 countX = ((CellLayout) parentLayout).getCountX();
113 countY = ((CellLayout) parentLayout).getCountY();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800114 } else {
Sunny Goyal290800b2015-03-05 11:33:33 -0800115 if (LauncherAppState.isDogfoodBuild()) {
116 throw new IllegalStateException("Parent of the focused item is not supported.");
117 } else {
118 return false;
119 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800120 }
Sunny Goyal290800b2015-03-05 11:33:33 -0800121
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800122 final int iconIndex = itemContainer.indexOfChild(v);
123 final PagedView container = (PagedView) parentLayout.getParent();
124 final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
125 final int pageCount = container.getChildCount();
126 ViewGroup newParent = null;
127 View child = null;
Hyunyoung Song38531712015-03-03 19:25:16 -0800128 // TODO(hyunyoungs): this matrix is not applicable on the last page.
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800129 int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
130
131 // Process focus.
132 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
133 iconIndex, pageIndex, pageCount);
134 if (newIconIndex == FocusLogic.NOOP) {
Sunny Goyal290800b2015-03-05 11:33:33 -0800135 handleNoopKey(keyCode, v);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800136 return consume;
137 }
138 switch (newIconIndex) {
Hyunyoung Song38531712015-03-03 19:25:16 -0800139 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
140 newParent = getAppsCustomizePage(container, pageIndex -1);
141 if (newParent != null) {
142 int row = FocusLogic.findRow(matrix, iconIndex);
143 container.snapToPage(pageIndex - 1);
144 // no need to create a new matrix.
145 child = newParent.getChildAt(matrix[countX-1][row]);
146 }
147 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800148 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
149 newParent = getAppsCustomizePage(container, pageIndex - 1);
150 if (newParent != null) {
151 container.snapToPage(pageIndex - 1);
152 child = newParent.getChildAt(0);
153 }
Hyunyoung Song38531712015-03-03 19:25:16 -0800154 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800155 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
156 newParent = getAppsCustomizePage(container, pageIndex - 1);
157 if (newParent != null) {
158 container.snapToPage(pageIndex - 1);
159 child = newParent.getChildAt(newParent.getChildCount() - 1);
160 }
161 break;
162 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
163 newParent = getAppsCustomizePage(container, pageIndex + 1);
164 if (newParent != null) {
165 container.snapToPage(pageIndex + 1);
166 child = newParent.getChildAt(0);
167 }
168 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800169 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
170 newParent = getAppsCustomizePage(container, pageIndex + 1);
171 if (newParent != null) {
172 container.snapToPage(pageIndex + 1);
173 int row = FocusLogic.findRow(matrix, iconIndex);
174 child = newParent.getChildAt(matrix[0][row]);
175 }
176 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800177 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
178 child = container.getChildAt(0);
179 break;
180 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
181 child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
182 break;
183 default: // Go to some item on the current page.
184 child = itemContainer.getChildAt(newIconIndex);
185 break;
186 }
187 if (child != null) {
188 child.requestFocus();
189 playSoundEffect(keyCode, v);
Sunny Goyal290800b2015-03-05 11:33:33 -0800190 } else {
191 handleNoopKey(keyCode, v);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800192 }
193 return consume;
194 }
195
Sunny Goyal290800b2015-03-05 11:33:33 -0800196 public void handleNoopKey(int keyCode, View v) { }
197 }
198
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800199 /**
200 * Handles key events in the workspace hot seat (bottom of the screen).
201 * <p>Currently we don't special case for the phone UI in different orientations, even though
202 * the hotseat is on the side in landscape mode. This is to ensure that accessibility
203 * consistency is maintained across rotations.
204 */
205 static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
206 boolean consume = FocusLogic.shouldConsume(keyCode);
207 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
208 return consume;
209 }
210 int orientation = v.getResources().getConfiguration().orientation;
211
212 if (DEBUG) {
213 Log.v(TAG, String.format(
214 "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, orientation=%d",
215 KeyEvent.keyCodeToString(keyCode), orientation));
216 }
217
218 // Initialize the variables.
219 final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
220 final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800221 Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
222
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800223 Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
224 int pageIndex = workspace.getCurrentPage();
225 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800226 int countX = -1;
227 int countY = -1;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800228 int iconIndex = findIndexOfView(hotseatParent, v);
229
230 final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
231 final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
232
233 ViewGroup parent = null;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800234 int[][] matrix = null;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800235
236 if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
237 orientation == Configuration.ORIENTATION_PORTRAIT) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800238 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
239 hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800240 iconIndex += iconParent.getChildCount();
241 countX = iconLayout.getCountX();
242 countY = iconLayout.getCountY() + hotseatLayout.getCountY();
243 parent = iconParent;
244 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
245 orientation == Configuration.ORIENTATION_LANDSCAPE) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800246 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
247 hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800248 iconIndex += iconParent.getChildCount();
249 countX = iconLayout.getCountX() + hotseatLayout.getCountX();
250 countY = iconLayout.getCountY();
251 parent = iconParent;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800252 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
253 orientation == Configuration.ORIENTATION_LANDSCAPE) {
254 keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
255 }else {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800256 // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
257 // matrix extended with hotseat.
258 matrix = FocusLogic.createSparseMatrix(hotseatLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800259 countX = hotseatLayout.getCountX();
260 countY = hotseatLayout.getCountY();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800261 parent = hotseatParent;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800262 }
263
264 // Process the focus.
265 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
266 iconIndex, pageIndex, pageCount);
267
Hyunyoung Song31178b82015-02-24 14:12:51 -0800268 View newIcon = null;
269 if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
270 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
271 newIcon = parent.getChildAt(0);
272 // TODO(hyunyoungs): handle cases where the child is not an icon but
273 // a folder or a widget.
274 workspace.snapToPage(pageIndex + 1);
275 }
276 if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800277 newIconIndex -= iconParent.getChildCount();
278 }
279 if (parent != null) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800280 if (newIcon == null && newIconIndex >=0) {
281 newIcon = parent.getChildAt(newIconIndex);
282 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800283 if (newIcon != null) {
284 newIcon.requestFocus();
285 playSoundEffect(keyCode, v);
286 }
287 }
288 return consume;
289 }
290
291 /**
292 * Handles key events in a workspace containing icons.
293 */
294 static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
295 boolean consume = FocusLogic.shouldConsume(keyCode);
296 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
297 return consume;
298 }
299 int orientation = v.getResources().getConfiguration().orientation;
300 if (DEBUG) {
301 Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] orientation=%d",
302 KeyEvent.keyCodeToString(keyCode), orientation));
303 }
304
305 // Initialize the variables.
306 ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
Hyunyoung Song38531712015-03-03 19:25:16 -0800307 CellLayout iconLayout = (CellLayout) parent.getParent();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800308 final Workspace workspace = (Workspace) iconLayout.getParent();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800309 final ViewGroup launcher = (ViewGroup) workspace.getParent();
310 final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800311 final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
312 int pageIndex = workspace.indexOfChild(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800313 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800314 int countX = iconLayout.getCountX();
315 int countY = iconLayout.getCountY();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800316 final int iconIndex = findIndexOfView(parent, v);
317
318 CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
319 ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
320 int[][] matrix;
321
Hyunyoung Song31178b82015-02-24 14:12:51 -0800322 // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800323 // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
324 // with the hotseat.
325 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN &&
326 orientation == Configuration.ORIENTATION_PORTRAIT) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800327 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
328 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800329 countY = countY + 1;
Hyunyoung Song31178b82015-02-24 14:12:51 -0800330 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800331 orientation == Configuration.ORIENTATION_LANDSCAPE) {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800332 matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
333 hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
334 countX = countX + 1;
335 } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
336 workspace.removeWorkspaceItem(v);
337 return consume;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800338 } else {
Hyunyoung Song31178b82015-02-24 14:12:51 -0800339 matrix = FocusLogic.createSparseMatrix(iconLayout);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800340 }
341
342 // Process the focus.
343 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
344 iconIndex, pageIndex, pageCount);
345 View newIcon = null;
346 switch (newIconIndex) {
347 case FocusLogic.NOOP:
348 if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
349 newIcon = tabs;
350 }
351 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800352 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
353 int row = FocusLogic.findRow(matrix, iconIndex);
354 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
355 if (parent != null) {
356 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800357 matrix = FocusLogic.createSparseMatrix(iconLayout,
Hyunyoung Song38531712015-03-03 19:25:16 -0800358 iconLayout.getCountX(), row);
359 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
360 FocusLogic.PIVOT, pageIndex - 1, pageCount);
361 newIcon = parent.getChildAt(newIconIndex);
362 }
363 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800364 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
365 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
366 newIcon = parent.getChildAt(0);
367 workspace.snapToPage(pageIndex - 1);
Hyunyoung Song38531712015-03-03 19:25:16 -0800368 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800369 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
370 parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
371 newIcon = parent.getChildAt(parent.getChildCount() - 1);
372 workspace.snapToPage(pageIndex - 1);
373 break;
374 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
375 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
376 newIcon = parent.getChildAt(0);
Hyunyoung Song31178b82015-02-24 14:12:51 -0800377 workspace.snapToPage(pageIndex + 1);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800378 break;
Hyunyoung Song38531712015-03-03 19:25:16 -0800379 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
380 row = FocusLogic.findRow(matrix, iconIndex);
381 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
382 if (parent != null) {
383 iconLayout = (CellLayout) parent.getParent();
Hyunyoung Songac721f82015-03-04 16:33:56 -0800384 matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
Hyunyoung Song38531712015-03-03 19:25:16 -0800385 newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
386 FocusLogic.PIVOT, pageIndex, pageCount);
387 newIcon = parent.getChildAt(newIconIndex);
388 }
389 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800390 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
391 newIcon = parent.getChildAt(0);
392 break;
393 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
394 newIcon = parent.getChildAt(parent.getChildCount() - 1);
395 break;
396 default:
397 // current page, some item.
398 if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
399 newIcon = parent.getChildAt(newIconIndex);
400 } else if (parent.getChildCount() <= newIconIndex &&
401 newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
402 newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
403 }
404 break;
405 }
406 if (newIcon != null) {
407 newIcon.requestFocus();
408 playSoundEffect(keyCode, v);
409 }
410 return consume;
411 }
412
413 /**
414 * Handles key events for items in a Folder.
415 */
416 static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
417 boolean consume = FocusLogic.shouldConsume(keyCode);
418 if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
419 return consume;
420 }
421 if (DEBUG) {
422 Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].",
423 KeyEvent.keyCodeToString(keyCode)));
424 }
425
426 // Initialize the variables.
427 ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
428 final CellLayout layout = (CellLayout) parent.getParent();
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800429 final Folder folder = (Folder) layout.getParent().getParent();
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800430 View title = folder.mFolderName;
431 Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
432 final int countX = layout.getCountX();
433 final int countY = layout.getCountY();
434 final int iconIndex = findIndexOfView(parent, v);
435 int pageIndex = workspace.indexOfChild(layout);
436 int pageCount = workspace.getChildCount();
Hyunyoung Song31178b82015-02-24 14:12:51 -0800437 int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */);
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800438
439 // Process the focus.
440 int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex,
441 pageIndex, pageCount);
442 View newIcon = null;
443 switch (newIconIndex) {
444 case FocusLogic.NOOP:
445 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
446 newIcon = title;
447 }
448 break;
449 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
450 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
451 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
452 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
453 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
454 if (DEBUG) {
455 Log.v(TAG, "Page advance handling not supported on folder icons.");
456 }
457 break;
458 default: // current page some item.
459 newIcon = parent.getChildAt(newIconIndex);
460 break;
461 }
462 if (newIcon != null) {
463 newIcon.requestFocus();
464 playSoundEffect(keyCode, v);
465 }
466 return consume;
467 }
468
469 //
470 // Helper methods.
471 //
472
Winson Chung97d85d22011-04-13 11:27:36 -0700473 /**
Adam Cohenae4f1552011-10-20 00:15:42 -0700474 * Returns the Viewgroup containing page contents for the page at the index specified.
475 */
476 private static ViewGroup getAppsCustomizePage(ViewGroup container, int index) {
477 ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index);
Winson Chungc58497e2013-09-03 17:48:37 -0700478 if (page instanceof CellLayout) {
Adam Cohenae4f1552011-10-20 00:15:42 -0700479 // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren
Winson Chungc58497e2013-09-03 17:48:37 -0700480 page = ((CellLayout) page).getShortcutsAndWidgets();
Adam Cohenae4f1552011-10-20 00:15:42 -0700481 }
482 return page;
483 }
484
485 /**
Winson Chung97d85d22011-04-13 11:27:36 -0700486 * Private helper method to get the CellLayoutChildren given a CellLayout index.
487 */
Michael Jurkaa52570f2012-03-20 03:18:20 -0700488 private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
489 ViewGroup container, int i) {
Sunny Goyalb3726d92014-08-20 16:58:17 -0700490 CellLayout parent = (CellLayout) container.getChildAt(i);
491 return parent.getShortcutsAndWidgets();
Winson Chung97d85d22011-04-13 11:27:36 -0700492 }
493
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800494 private static int findIndexOfView(ViewGroup parent, View v) {
495 for (int i = 0; i < parent.getChildCount(); i++) {
496 if (v != null && v.equals(parent.getChildAt(i))) {
497 return i;
Winson Chung97d85d22011-04-13 11:27:36 -0700498 }
499 }
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800500 return -1;
Winson Chung97d85d22011-04-13 11:27:36 -0700501 }
502
503 /**
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800504 * Helper method to be used for playing sound effects.
Winson Chung97d85d22011-04-13 11:27:36 -0700505 */
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800506 private static void playSoundEffect(int keyCode, View v) {
Winson Chung97d85d22011-04-13 11:27:36 -0700507 switch (keyCode) {
508 case KeyEvent.KEYCODE_DPAD_LEFT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800509 v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
Winson Chung97d85d22011-04-13 11:27:36 -0700510 break;
511 case KeyEvent.KEYCODE_DPAD_RIGHT:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800512 v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
Winson Chung97d85d22011-04-13 11:27:36 -0700513 break;
514 case KeyEvent.KEYCODE_DPAD_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700515 case KeyEvent.KEYCODE_PAGE_DOWN:
Winson Chung97d85d22011-04-13 11:27:36 -0700516 case KeyEvent.KEYCODE_MOVE_END:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800517 v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
Adam Cohenac56cff2011-09-28 20:45:37 -0700518 break;
519 case KeyEvent.KEYCODE_DPAD_UP:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800520 case KeyEvent.KEYCODE_PAGE_UP:
Adam Cohenac56cff2011-09-28 20:45:37 -0700521 case KeyEvent.KEYCODE_MOVE_HOME:
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800522 v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
Adam Cohenac56cff2011-09-28 20:45:37 -0700523 break;
Hyunyoung Songee3e6a72015-02-20 14:25:27 -0800524 default:
Winson Chung97d85d22011-04-13 11:27:36 -0700525 break;
Winson Chung97d85d22011-04-13 11:27:36 -0700526 }
Winson Chung97d85d22011-04-13 11:27:36 -0700527 }
528}