| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3; |
| |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.SoundEffectConstants; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import com.android.launcher3.config.FeatureFlags; |
| import com.android.launcher3.folder.Folder; |
| import com.android.launcher3.folder.FolderPagedView; |
| import com.android.launcher3.util.FocusLogic; |
| import com.android.launcher3.util.Thunk; |
| |
| /** |
| * A keyboard listener we set on all the workspace icons. |
| */ |
| class IconKeyEventListener implements View.OnKeyListener { |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| return FocusHelper.handleIconKeyEvent(v, keyCode, event); |
| } |
| } |
| |
| /** |
| * A keyboard listener we set on all the hotseat buttons. |
| */ |
| class HotseatIconKeyEventListener implements View.OnKeyListener { |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event); |
| } |
| } |
| |
| /** |
| * A keyboard listener we set on full screen pages (e.g. custom content). |
| */ |
| class FullscreenKeyEventListener implements View.OnKeyListener { |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT |
| || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) { |
| // Handle the key event just like a workspace icon would in these cases. In this case, |
| // it will basically act as if there is a single icon in the top left (so you could |
| // think of the fullscreen page as a focusable fullscreen widget). |
| return FocusHelper.handleIconKeyEvent(v, keyCode, event); |
| } |
| return false; |
| } |
| } |
| |
| public class FocusHelper { |
| |
| private static final String TAG = "FocusHelper"; |
| private static final boolean DEBUG = false; |
| |
| /** |
| * Handles key events in paged folder. |
| */ |
| public static class PagedFolderKeyEventListener implements View.OnKeyListener { |
| |
| private final Folder mFolder; |
| |
| public PagedFolderKeyEventListener(Folder folder) { |
| mFolder = folder; |
| } |
| |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent e) { |
| boolean consume = FocusLogic.shouldConsume(keyCode); |
| if (e.getAction() == KeyEvent.ACTION_UP) { |
| return consume; |
| } |
| if (DEBUG) { |
| Log.v(TAG, String.format("Handle ALL Folders keyevent=[%s].", |
| KeyEvent.keyCodeToString(keyCode))); |
| } |
| |
| if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) { |
| if (FeatureFlags.IS_DOGFOOD_BUILD) { |
| throw new IllegalStateException("Parent of the focused item is not supported."); |
| } else { |
| return false; |
| } |
| } |
| |
| // Initialize variables. |
| final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent(); |
| final CellLayout cellLayout = (CellLayout) itemContainer.getParent(); |
| |
| final int iconIndex = itemContainer.indexOfChild(v); |
| final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent(); |
| |
| final int pageIndex = pagedView.indexOfChild(cellLayout); |
| final int pageCount = pagedView.getPageCount(); |
| final boolean isLayoutRtl = Utilities.isRtl(v.getResources()); |
| |
| int[][] matrix = FocusLogic.createSparseMatrix(cellLayout); |
| // Process focus. |
| int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex, |
| pageCount, isLayoutRtl); |
| if (newIconIndex == FocusLogic.NOOP) { |
| handleNoopKey(keyCode, v); |
| return consume; |
| } |
| ShortcutAndWidgetContainer newParent = null; |
| View child = null; |
| |
| switch (newIconIndex) { |
| case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: |
| case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: |
| newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); |
| if (newParent != null) { |
| int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; |
| pagedView.snapToPage(pageIndex - 1); |
| child = newParent.getChildAt( |
| ((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) |
| ^ newParent.invertLayoutHorizontally()) ? 0 : matrix.length - 1, |
| row); |
| } |
| break; |
| case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: |
| newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); |
| if (newParent != null) { |
| pagedView.snapToPage(pageIndex - 1); |
| child = newParent.getChildAt(0, 0); |
| } |
| break; |
| case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: |
| newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); |
| if (newParent != null) { |
| pagedView.snapToPage(pageIndex - 1); |
| child = newParent.getChildAt(matrix.length - 1, matrix[0].length - 1); |
| } |
| break; |
| case FocusLogic.NEXT_PAGE_FIRST_ITEM: |
| newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1); |
| if (newParent != null) { |
| pagedView.snapToPage(pageIndex + 1); |
| child = newParent.getChildAt(0, 0); |
| } |
| break; |
| case FocusLogic.NEXT_PAGE_LEFT_COLUMN: |
| case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: |
| newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1); |
| if (newParent != null) { |
| pagedView.snapToPage(pageIndex + 1); |
| child = FocusLogic.getAdjacentChildInNextFolderPage( |
| newParent, v, newIconIndex); |
| } |
| break; |
| case FocusLogic.CURRENT_PAGE_FIRST_ITEM: |
| child = cellLayout.getChildAt(0, 0); |
| break; |
| case FocusLogic.CURRENT_PAGE_LAST_ITEM: |
| child = pagedView.getLastItem(); |
| break; |
| default: // Go to some item on the current page. |
| child = itemContainer.getChildAt(newIconIndex); |
| break; |
| } |
| if (child != null) { |
| child.requestFocus(); |
| playSoundEffect(keyCode, v); |
| } else { |
| handleNoopKey(keyCode, v); |
| } |
| return consume; |
| } |
| |
| public void handleNoopKey(int keyCode, View v) { |
| if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { |
| mFolder.mFolderName.requestFocus(); |
| playSoundEffect(keyCode, v); |
| } |
| } |
| } |
| |
| /** |
| * Handles key events in the workspace hotseat (bottom of the screen). |
| * <p>Currently we don't special case for the phone UI in different orientations, even though |
| * the hotseat is on the side in landscape mode. This is to ensure that accessibility |
| * consistency is maintained across rotations. |
| */ |
| static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) { |
| boolean consume = FocusLogic.shouldConsume(keyCode); |
| if (e.getAction() == KeyEvent.ACTION_UP || !consume) { |
| return consume; |
| } |
| |
| final Launcher launcher = Launcher.getLauncher(v.getContext()); |
| final DeviceProfile profile = launcher.getDeviceProfile(); |
| |
| if (DEBUG) { |
| Log.v(TAG, String.format( |
| "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s", |
| KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout())); |
| } |
| |
| // Initialize the variables. |
| final Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); |
| final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent(); |
| final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent(); |
| |
| final ItemInfo itemInfo = (ItemInfo) v.getTag(); |
| int pageIndex = workspace.getNextPage(); |
| int pageCount = workspace.getChildCount(); |
| int iconIndex = hotseatParent.indexOfChild(v); |
| int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets() |
| .getChildAt(iconIndex).getLayoutParams()).cellX; |
| |
| final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex); |
| if (iconLayout == null) { |
| // This check is to guard against cases where key strokes rushes in when workspace |
| // child creation/deletion is still in flux. (e.g., during drop or fling |
| // animation.) |
| return consume; |
| } |
| final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); |
| |
| ViewGroup parent = null; |
| int[][] matrix = null; |
| |
| if (keyCode == KeyEvent.KEYCODE_DPAD_UP && |
| !profile.isVerticalBarLayout()) { |
| matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile); |
| iconIndex += iconParent.getChildCount(); |
| parent = iconParent; |
| } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && |
| profile.isVerticalBarLayout()) { |
| matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile); |
| iconIndex += iconParent.getChildCount(); |
| parent = iconParent; |
| } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && |
| profile.isVerticalBarLayout()) { |
| keyCode = KeyEvent.KEYCODE_PAGE_DOWN; |
| } else { |
| // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the |
| // matrix extended with hotseat. |
| matrix = FocusLogic.createSparseMatrix(hotseatLayout); |
| parent = hotseatParent; |
| } |
| |
| // Process the focus. |
| int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex, |
| pageCount, Utilities.isRtl(v.getResources())); |
| |
| View newIcon = null; |
| switch (newIconIndex) { |
| case FocusLogic.NEXT_PAGE_FIRST_ITEM: |
| parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); |
| newIcon = parent.getChildAt(0); |
| // TODO(hyunyoungs): handle cases where the child is not an icon but |
| // a folder or a widget. |
| workspace.snapToPage(pageIndex + 1); |
| break; |
| case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: |
| parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); |
| newIcon = parent.getChildAt(0); |
| // TODO(hyunyoungs): handle cases where the child is not an icon but |
| // a folder or a widget. |
| workspace.snapToPage(pageIndex - 1); |
| break; |
| case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: |
| parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); |
| newIcon = parent.getChildAt(parent.getChildCount() - 1); |
| // TODO(hyunyoungs): handle cases where the child is not an icon but |
| // a folder or a widget. |
| workspace.snapToPage(pageIndex - 1); |
| break; |
| case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: |
| case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: |
| // Go to the previous page but keep the focus on the same hotseat icon. |
| workspace.snapToPage(pageIndex - 1); |
| break; |
| case FocusLogic.NEXT_PAGE_LEFT_COLUMN: |
| case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: |
| // Go to the next page but keep the focus on the same hotseat icon. |
| workspace.snapToPage(pageIndex + 1); |
| break; |
| } |
| if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) { |
| newIconIndex -= iconParent.getChildCount(); |
| } |
| if (parent != null) { |
| if (newIcon == null && newIconIndex >= 0) { |
| newIcon = parent.getChildAt(newIconIndex); |
| } |
| if (newIcon != null) { |
| newIcon.requestFocus(); |
| playSoundEffect(keyCode, v); |
| } |
| } |
| return consume; |
| } |
| |
| /** |
| * Handles key events in a workspace containing icons. |
| */ |
| static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) { |
| boolean consume = FocusLogic.shouldConsume(keyCode); |
| if (e.getAction() == KeyEvent.ACTION_UP || !consume) { |
| return consume; |
| } |
| |
| Launcher launcher = Launcher.getLauncher(v.getContext()); |
| DeviceProfile profile = launcher.getDeviceProfile(); |
| |
| if (DEBUG) { |
| Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s", |
| KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout())); |
| } |
| |
| // Initialize the variables. |
| ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); |
| CellLayout iconLayout = (CellLayout) parent.getParent(); |
| final Workspace workspace = (Workspace) iconLayout.getParent(); |
| final ViewGroup dragLayer = (ViewGroup) workspace.getParent(); |
| final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.drop_target_bar); |
| final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat); |
| |
| final ItemInfo itemInfo = (ItemInfo) v.getTag(); |
| final int iconIndex = parent.indexOfChild(v); |
| final int pageIndex = workspace.indexOfChild(iconLayout); |
| final int pageCount = workspace.getChildCount(); |
| |
| CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0); |
| ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets(); |
| int[][] matrix; |
| |
| // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed |
| // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended |
| // with the hotseat. |
| if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) { |
| matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile); |
| } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && |
| profile.isVerticalBarLayout()) { |
| matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile); |
| } else { |
| matrix = FocusLogic.createSparseMatrix(iconLayout); |
| } |
| |
| // Process the focus. |
| int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex, |
| pageCount, Utilities.isRtl(v.getResources())); |
| boolean isRtl = Utilities.isRtl(v.getResources()); |
| View newIcon = null; |
| CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex); |
| switch (newIconIndex) { |
| case FocusLogic.NOOP: |
| if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { |
| newIcon = tabs; |
| } |
| break; |
| case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: |
| case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: |
| int newPageIndex = pageIndex - 1; |
| if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) { |
| newPageIndex = pageIndex + 1; |
| } |
| int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; |
| parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); |
| if (parent != null) { |
| iconLayout = (CellLayout) parent.getParent(); |
| matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, |
| iconLayout.getCountX(), row); |
| newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT, |
| newPageIndex, pageCount, Utilities.isRtl(v.getResources())); |
| if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) { |
| newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, |
| isRtl); |
| } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) { |
| newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, |
| isRtl); |
| } else { |
| newIcon = parent.getChildAt(newIconIndex); |
| } |
| } |
| break; |
| case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: |
| workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1); |
| newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl); |
| if (newIcon == null) { |
| // Check the hotseat if no focusable item was found on the workspace. |
| newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl); |
| workspace.snapToPage(pageIndex - 1); |
| } |
| break; |
| case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: |
| newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, isRtl); |
| break; |
| case FocusLogic.NEXT_PAGE_FIRST_ITEM: |
| newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, isRtl); |
| break; |
| case FocusLogic.NEXT_PAGE_LEFT_COLUMN: |
| case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: |
| newPageIndex = pageIndex + 1; |
| if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) { |
| newPageIndex = pageIndex - 1; |
| } |
| row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; |
| parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); |
| if (parent != null) { |
| iconLayout = (CellLayout) parent.getParent(); |
| matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, -1, row); |
| newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT, |
| newPageIndex, pageCount, Utilities.isRtl(v.getResources())); |
| if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) { |
| newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, |
| isRtl); |
| } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) { |
| newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, |
| isRtl); |
| } else { |
| newIcon = parent.getChildAt(newIconIndex); |
| } |
| } |
| break; |
| case FocusLogic.CURRENT_PAGE_FIRST_ITEM: |
| newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl); |
| if (newIcon == null) { |
| // Check the hotseat if no focusable item was found on the workspace. |
| newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl); |
| } |
| break; |
| case FocusLogic.CURRENT_PAGE_LAST_ITEM: |
| newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl); |
| if (newIcon == null) { |
| // Check the hotseat if no focusable item was found on the workspace. |
| newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout, isRtl); |
| } |
| break; |
| default: |
| // current page, some item. |
| if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) { |
| newIcon = parent.getChildAt(newIconIndex); |
| } else if (parent.getChildCount() <= newIconIndex && |
| newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) { |
| newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount()); |
| } |
| break; |
| } |
| if (newIcon != null) { |
| newIcon.requestFocus(); |
| playSoundEffect(keyCode, v); |
| } |
| return consume; |
| } |
| |
| // |
| // Helper methods. |
| // |
| |
| /** |
| * Private helper method to get the CellLayoutChildren given a CellLayout index. |
| */ |
| @Thunk static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( |
| ViewGroup container, int i) { |
| CellLayout parent = (CellLayout) container.getChildAt(i); |
| return parent.getShortcutsAndWidgets(); |
| } |
| |
| /** |
| * Helper method to be used for playing sound effects. |
| */ |
| @Thunk static void playSoundEffect(int keyCode, View v) { |
| switch (keyCode) { |
| case KeyEvent.KEYCODE_DPAD_LEFT: |
| v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); |
| break; |
| case KeyEvent.KEYCODE_DPAD_RIGHT: |
| v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); |
| break; |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| case KeyEvent.KEYCODE_PAGE_DOWN: |
| case KeyEvent.KEYCODE_MOVE_END: |
| v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); |
| break; |
| case KeyEvent.KEYCODE_DPAD_UP: |
| case KeyEvent.KEYCODE_PAGE_UP: |
| case KeyEvent.KEYCODE_MOVE_HOME: |
| v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout, |
| int pageIndex, boolean isRtl) { |
| if (pageIndex - 1 < 0) { |
| return null; |
| } |
| CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1); |
| View newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl); |
| if (newIcon == null) { |
| // Check the hotseat if no focusable item was found on the workspace. |
| newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout,isRtl); |
| workspace.snapToPage(pageIndex - 1); |
| } |
| return newIcon; |
| } |
| |
| private static View handleNextPageFirstItem(Workspace workspace, CellLayout hotseatLayout, |
| int pageIndex, boolean isRtl) { |
| if (pageIndex + 1 >= workspace.getPageCount()) { |
| return null; |
| } |
| CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex + 1); |
| View newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl); |
| if (newIcon == null) { |
| // Check the hotseat if no focusable item was found on the workspace. |
| newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl); |
| workspace.snapToPage(pageIndex + 1); |
| } |
| return newIcon; |
| } |
| |
| private static View getFirstFocusableIconInReadingOrder(CellLayout cellLayout, boolean isRtl) { |
| View icon; |
| int countX = cellLayout.getCountX(); |
| for (int y = 0; y < cellLayout.getCountY(); y++) { |
| int increment = isRtl ? -1 : 1; |
| for (int x = isRtl ? countX - 1 : 0; 0 <= x && x < countX; x += increment) { |
| if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) { |
| return icon; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static View getFirstFocusableIconInReverseReadingOrder(CellLayout cellLayout, |
| boolean isRtl) { |
| View icon; |
| int countX = cellLayout.getCountX(); |
| for (int y = cellLayout.getCountY() - 1; y >= 0; y--) { |
| int increment = isRtl ? 1 : -1; |
| for (int x = isRtl ? 0 : countX - 1; 0 <= x && x < countX; x += increment) { |
| if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) { |
| return icon; |
| } |
| } |
| } |
| return null; |
| } |
| } |