From 11b5f6053f321eccfd27ce394d7902a324a8351b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 29 Jan 2021 17:01:19 -0800 Subject: [PATCH] Removing unused keyboard handling code Bug: 178781566 Test: Presubmit Change-Id: I997a7980f4ae2cef1e599ca2cadffc56df5f296e --- .../launcher3/AppWidgetResizeFrame.java | 13 +- src/com/android/launcher3/FocusHelper.java | 567 ------------------ .../android/launcher3/util/FocusLogic.java | 553 ----------------- 3 files changed, 11 insertions(+), 1122 deletions(-) delete mode 100644 src/com/android/launcher3/FocusHelper.java delete mode 100644 src/com/android/launcher3/util/FocusLogic.java diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index bc3e341806..8df5412d37 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -21,7 +21,6 @@ import android.view.ViewGroup; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.util.FocusLogic; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.widget.LauncherAppWidgetHostView; @@ -476,7 +475,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O @Override public boolean onKey(View v, int keyCode, KeyEvent event) { // Clear the frame and give focus to the widget host view when a directional key is pressed. - if (FocusLogic.shouldConsume(keyCode)) { + if (shouldConsume(keyCode)) { close(false); mWidgetView.requestFocus(); return true; @@ -599,4 +598,14 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O return moveEnd ? out.size() - size() : size() - out.size(); } } + + /** + * Returns true only if this utility class handles the key code. + */ + public static boolean shouldConsume(int keyCode) { + return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT + || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN + || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END + || keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN); + } } diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java deleted file mode 100644 index e5aecf7be1..0000000000 --- a/src/com/android/launcher3/FocusHelper.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * 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.model.data.ItemInfo; -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; - } -} - -/** - * TODO: Reevaluate if this is still required - */ -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_STUDIO_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). - *

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; - } -} diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java deleted file mode 100644 index 4f4cccd7a9..0000000000 --- a/src/com/android/launcher3/util/FocusLogic.java +++ /dev/null @@ -1,553 +0,0 @@ -/* - * 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.util; - -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; - -import com.android.launcher3.CellLayout; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.config.FeatureFlags; - -import java.util.Arrays; - -/** - * Calculates the next item that a {@link KeyEvent} should change the focus to. - *

- * Note, this utility class calculates everything regards to icon index and its (x,y) coordinates. - * Currently supports: - *

- * *

- * For testing, one can use a BT keyboard, or use following adb command. - * ex. $ adb shell input keyevent 20 // KEYCODE_DPAD_LEFT - */ -public class FocusLogic { - - private static final String TAG = "FocusLogic"; - private static final boolean DEBUG = false; - - /** Item and page index related constant used by {@link #handleKeyEvent}. */ - public static final int NOOP = -1; - - public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2; - public static final int PREVIOUS_PAGE_FIRST_ITEM = -3; - public static final int PREVIOUS_PAGE_LAST_ITEM = -4; - public static final int PREVIOUS_PAGE_LEFT_COLUMN = -5; - - public static final int CURRENT_PAGE_FIRST_ITEM = -6; - public static final int CURRENT_PAGE_LAST_ITEM = -7; - - public static final int NEXT_PAGE_FIRST_ITEM = -8; - public static final int NEXT_PAGE_LEFT_COLUMN = -9; - public static final int NEXT_PAGE_RIGHT_COLUMN = -10; - - public static final int ALL_APPS_COLUMN = -11; - - // Matrix related constant. - public static final int EMPTY = -1; - public static final int PIVOT = 100; - - /** - * Returns true only if this utility class handles the key code. - */ - public static boolean shouldConsume(int keyCode) { - return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || - keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || - keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END || - keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN); - } - - public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex, - int pageCount, boolean isRtl) { - - int cntX = map == null ? -1 : map.length; - int cntY = map == null ? -1 : map[0].length; - - if (DEBUG) { - Log.v(TAG, String.format( - "handleKeyEvent START: cntX=%d, cntY=%d, iconIdx=%d, pageIdx=%d, pageCnt=%d", - cntX, cntY, iconIdx, pageIndex, pageCount)); - } - - int newIndex = NOOP; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/, isRtl); - if (!isRtl && newIndex == NOOP && pageIndex > 0) { - newIndex = PREVIOUS_PAGE_RIGHT_COLUMN; - } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) { - newIndex = NEXT_PAGE_RIGHT_COLUMN; - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/, isRtl); - if (!isRtl && newIndex == NOOP && pageIndex < pageCount - 1) { - newIndex = NEXT_PAGE_LEFT_COLUMN; - } else if (isRtl && newIndex == NOOP && pageIndex > 0) { - newIndex = PREVIOUS_PAGE_LEFT_COLUMN; - } - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, 1 /*increment*/); - break; - case KeyEvent.KEYCODE_DPAD_UP: - newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, -1 /*increment*/); - break; - case KeyEvent.KEYCODE_MOVE_HOME: - newIndex = handleMoveHome(); - break; - case KeyEvent.KEYCODE_MOVE_END: - newIndex = handleMoveEnd(); - break; - case KeyEvent.KEYCODE_PAGE_DOWN: - newIndex = handlePageDown(pageIndex, pageCount); - break; - case KeyEvent.KEYCODE_PAGE_UP: - newIndex = handlePageUp(pageIndex); - break; - default: - break; - } - - if (DEBUG) { - Log.v(TAG, String.format("handleKeyEvent FINISH: index [%d -> %s]", - iconIdx, getStringIndex(newIndex))); - } - return newIndex; - } - - /** - * Returns a matrix of size (m x n) that has been initialized with {@link #EMPTY}. - * - * @param m number of columns in the matrix - * @param n number of rows in the matrix - */ - // TODO: get rid of dynamic matrix creation. - private static int[][] createFullMatrix(int m, int n) { - int[][] matrix = new int [m][n]; - - for (int i=0; i < m;i++) { - Arrays.fill(matrix[i], EMPTY); - } - return matrix; - } - - /** - * Returns a matrix of size same as the {@link CellLayout} dimension that is initialized with the - * index of the child view. - */ - // TODO: get rid of the dynamic matrix creation - public static int[][] createSparseMatrix(CellLayout layout) { - ShortcutAndWidgetContainer parent = layout.getShortcutsAndWidgets(); - final int m = layout.getCountX(); - final int n = layout.getCountY(); - final boolean invert = parent.invertLayoutHorizontally(); - - int[][] matrix = createFullMatrix(m, n); - - // Iterate thru the children. - for (int i = 0; i < parent.getChildCount(); i++ ) { - View cell = parent.getChildAt(i); - if (!cell.isFocusable()) { - continue; - } - int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX; - int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY; - int x = invert ? (m - cx - 1) : cx; - if (x < m && cy < n) { // check if view fits into matrix, else skip - matrix[x][cy] = i; - } - } - if (DEBUG) { - printMatrix(matrix); - } - return matrix; - } - - /** - * Creates a sparse matrix that merges the icon and hotseat view group using the cell layout. - * The size of the returning matrix is [icon column count x (icon + hotseat row count)] - * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)] - */ - // TODO: get rid of the dynamic matrix creation - public static int[][] createSparseMatrixWithHotseat( - CellLayout iconLayout, CellLayout hotseatLayout, DeviceProfile dp) { - - ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); - ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets(); - - boolean isHotseatHorizontal = !dp.isVerticalBarLayout(); - - int m, n; - if (isHotseatHorizontal) { - m = hotseatLayout.getCountX(); - n = iconLayout.getCountY() + hotseatLayout.getCountY(); - } else { - m = iconLayout.getCountX() + hotseatLayout.getCountX(); - n = hotseatLayout.getCountY(); - } - int[][] matrix = createFullMatrix(m, n); - // Iterate through the children of the workspace. - for (int i = 0; i < iconParent.getChildCount(); i++) { - View cell = iconParent.getChildAt(i); - if (!cell.isFocusable()) { - continue; - } - int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX; - int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY; - matrix[cx][cy] = i; - } - - // Iterate thru the children of the hotseat. - for (int i = hotseatParent.getChildCount() - 1; i >= 0; i--) { - if (isHotseatHorizontal) { - int cx = ((CellLayout.LayoutParams) - hotseatParent.getChildAt(i).getLayoutParams()).cellX; - matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i; - } else { - int cy = ((CellLayout.LayoutParams) - hotseatParent.getChildAt(i).getLayoutParams()).cellY; - matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i; - } - } - if (DEBUG) { - printMatrix(matrix); - } - return matrix; - } - - /** - * Creates a sparse matrix that merges the icon of previous/next page and last column of - * current page. When left key is triggered on the leftmost column, sparse matrix is created - * that combines previous page matrix and an extra column on the right. Likewise, when right - * key is triggered on the rightmost column, sparse matrix is created that combines this column - * on the 0th column and the next page matrix. - * - * @param pivotX x coordinate of the focused item in the current page - * @param pivotY y coordinate of the focused item in the current page - */ - // TODO: get rid of the dynamic matrix creation - public static int[][] createSparseMatrixWithPivotColumn(CellLayout iconLayout, - int pivotX, int pivotY) { - - ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); - - int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY()); - - // Iterate thru the children of the top parent. - for (int i = 0; i < iconParent.getChildCount(); i++) { - View cell = iconParent.getChildAt(i); - if (!cell.isFocusable()) { - continue; - } - int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX; - int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY; - if (pivotX < 0) { - matrix[cx - pivotX][cy] = i; - } else { - matrix[cx][cy] = i; - } - } - - if (pivotX < 0) { - matrix[0][pivotY] = PIVOT; - } else { - matrix[pivotX][pivotY] = PIVOT; - } - if (DEBUG) { - printMatrix(matrix); - } - return matrix; - } - - // - // key event handling methods. - // - - /** - * Calculates icon that has is closest to the horizontal axis in reference to the cur icon. - * - * Example of the check order for KEYCODE_DPAD_RIGHT: - * [ ][ ][13][14][15] - * [ ][ 6][ 8][10][12] - * [ X][ 1][ 2][ 3][ 4] - * [ ][ 5][ 7][ 9][11] - */ - // TODO: add unit tests to verify all permutation. - private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY, - int[][] matrix, int increment, boolean isRtl) { - if(matrix == null) { - throw new IllegalStateException("Dpad navigation requires a matrix."); - } - int newIconIndex = NOOP; - - int xPos = -1; - int yPos = -1; - // Figure out the location of the icon. - for (int i = 0; i < cntX; i++) { - for (int j = 0; j < cntY; j++) { - if (matrix[i][j] == iconIdx) { - xPos = i; - yPos = j; - } - } - } - if (DEBUG) { - Log.v(TAG, String.format("\thandleDpadHorizontal: \t[x, y]=[%d, %d] iconIndex=%d", - xPos, yPos, iconIdx)); - } - - // Rule1: check first in the horizontal direction - for (int x = xPos + increment; 0 <= x && x < cntX; x += increment) { - if ((newIconIndex = inspectMatrix(x, yPos, cntX, cntY, matrix)) != NOOP - && newIconIndex != ALL_APPS_COLUMN) { - return newIconIndex; - } - } - - // Rule2: check (x1-n, yPos + increment), (x1-n, yPos - increment) - // (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment) - int nextYPos1; - int nextYPos2; - boolean haveCrossedAllAppsColumn1 = false; - boolean haveCrossedAllAppsColumn2 = false; - int x = -1; - for (int coeff = 1; coeff < cntY; coeff++) { - nextYPos1 = yPos + coeff * increment; - nextYPos2 = yPos - coeff * increment; - x = xPos + increment * coeff; - if (inspectMatrix(x, nextYPos1, cntX, cntY, matrix) == ALL_APPS_COLUMN) { - haveCrossedAllAppsColumn1 = true; - } - if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) { - haveCrossedAllAppsColumn2 = true; - } - for (; 0 <= x && x < cntX; x += increment) { - int offset1 = haveCrossedAllAppsColumn1 && x < cntX - 1 ? increment : 0; - newIconIndex = inspectMatrix(x, nextYPos1 + offset1, cntX, cntY, matrix); - if (newIconIndex != NOOP) { - return newIconIndex; - } - int offset2 = haveCrossedAllAppsColumn2 && x < cntX - 1 ? -increment : 0; - newIconIndex = inspectMatrix(x, nextYPos2 + offset2, cntX, cntY, matrix); - if (newIconIndex != NOOP) { - return newIconIndex; - } - } - } - - // Rule3: if switching between pages, do a brute-force search to find an item that was - // missed by rules 1 and 2 (such as when going from a bottom right icon to top left) - if (iconIdx == PIVOT) { - if (isRtl) { - return increment < 0 ? NEXT_PAGE_FIRST_ITEM : PREVIOUS_PAGE_LAST_ITEM; - } - return increment < 0 ? PREVIOUS_PAGE_LAST_ITEM : NEXT_PAGE_FIRST_ITEM; - } - return newIconIndex; - } - - /** - * Calculates icon that is closest to the vertical axis in reference to the current icon. - * - * Example of the check order for KEYCODE_DPAD_DOWN: - * [ ][ ][ ][ X][ ][ ][ ] - * [ ][ ][ 5][ 1][ 4][ ][ ] - * [ ][10][ 7][ 2][ 6][ 9][ ] - * [14][12][ 9][ 3][ 8][11][13] - */ - // TODO: add unit tests to verify all permutation. - private static int handleDpadVertical(int iconIndex, int cntX, int cntY, - int [][] matrix, int increment) { - int newIconIndex = NOOP; - if(matrix == null) { - throw new IllegalStateException("Dpad navigation requires a matrix."); - } - - int xPos = -1; - int yPos = -1; - // Figure out the location of the icon. - for (int i = 0; i< cntX; i++) { - for (int j = 0; j < cntY; j++) { - if (matrix[i][j] == iconIndex) { - xPos = i; - yPos = j; - } - } - } - - if (DEBUG) { - Log.v(TAG, String.format("\thandleDpadVertical: \t[x, y]=[%d, %d] iconIndex=%d", - xPos, yPos, iconIndex)); - } - - // Rule1: check first in the dpad direction - for (int y = yPos + increment; 0 <= y && y 0) { - return PREVIOUS_PAGE_FIRST_ITEM; - } else { - return CURRENT_PAGE_FIRST_ITEM; - } - } - - // - // Helper methods. - // - - private static boolean isValid(int xPos, int yPos, int countX, int countY) { - return (0 <= xPos && xPos < countX && 0 <= yPos && yPos < countY); - } - - private static int inspectMatrix(int x, int y, int cntX, int cntY, int[][] matrix) { - int newIconIndex = NOOP; - if (isValid(x, y, cntX, cntY)) { - if (matrix[x][y] != -1) { - newIconIndex = matrix[x][y]; - if (DEBUG) { - Log.v(TAG, String.format("\t\tinspect: \t[x, y]=[%d, %d] %d", - x, y, matrix[x][y])); - } - return newIconIndex; - } - } - return newIconIndex; - } - - /** - * Only used for debugging. - */ - private static String getStringIndex(int index) { - switch(index) { - case NOOP: return "NOOP"; - case PREVIOUS_PAGE_FIRST_ITEM: return "PREVIOUS_PAGE_FIRST"; - case PREVIOUS_PAGE_LAST_ITEM: return "PREVIOUS_PAGE_LAST"; - case PREVIOUS_PAGE_RIGHT_COLUMN:return "PREVIOUS_PAGE_RIGHT_COLUMN"; - case CURRENT_PAGE_FIRST_ITEM: return "CURRENT_PAGE_FIRST"; - case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST"; - case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST"; - case NEXT_PAGE_LEFT_COLUMN: return "NEXT_PAGE_LEFT_COLUMN"; - case ALL_APPS_COLUMN: return "ALL_APPS_COLUMN"; - default: - return Integer.toString(index); - } - } - - /** - * Only used for debugging. - */ - private static void printMatrix(int[][] matrix) { - Log.v(TAG, "\tprintMap:"); - int m = matrix.length; - int n = matrix[0].length; - - for (int j=0; j < n; j++) { - String colY = "\t\t"; - for (int i=0; i < m; i++) { - colY += String.format("%3d",matrix[i][j]); - } - Log.v(TAG, colY); - } - } - - /** - * @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or - * {@link #NEXT_PAGE_RIGHT_COLUMN} - * @return the view adjacent to {@param oldView} in the {@param nextPage} of the folder. - */ - public static View getAdjacentChildInNextFolderPage( - ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) { - final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY; - - int column = (edgeColumn == NEXT_PAGE_LEFT_COLUMN) ^ nextPage.invertLayoutHorizontally() - ? 0 : (((CellLayout) nextPage.getParent()).getCountX() - 1); - - for (; column >= 0; column--) { - for (int row = newRow; row >= 0; row--) { - View newView = nextPage.getChildAt(column, row); - if (newView != null) { - return newView; - } - } - } - return null; - } -}