Loading quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.quickstep.util; import static java.lang.annotation.RetentionPolicy.SOURCE; import androidx.annotation.IntDef; import com.android.launcher3.util.IntArray; import java.lang.annotation.Retention; /** * Helper class for navigating RecentsView grid tasks via arrow keys and tab. */ public class TaskGridNavHelper { public static final int CLEAR_ALL_PLACEHOLDER_ID = -1; public static final int INVALID_FOCUSED_TASK_ID = -1; public static final int DIRECTION_UP = 0; public static final int DIRECTION_DOWN = 1; public static final int DIRECTION_LEFT = 2; public static final int DIRECTION_RIGHT = 3; public static final int DIRECTION_TAB = 4; @Retention(SOURCE) @IntDef({DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_TAB}) public @interface TASK_NAV_DIRECTION {} private final IntArray mOriginalTopRowIds; private IntArray mTopRowIds; private IntArray mBottomRowIds; private final int mFocusedTaskId; public TaskGridNavHelper(IntArray topIds, IntArray bottomIds, int focusedTaskId) { mFocusedTaskId = focusedTaskId; mOriginalTopRowIds = topIds.clone(); generateTaskViewIdGrid(topIds, bottomIds); } private void generateTaskViewIdGrid(IntArray topRowIdArray, IntArray bottomRowIdArray) { boolean hasFocusedTask = mFocusedTaskId != INVALID_FOCUSED_TASK_ID; int maxSize = Math.max(topRowIdArray.size(), bottomRowIdArray.size()) + (hasFocusedTask ? 1 : 0); int minSize = Math.min(topRowIdArray.size(), bottomRowIdArray.size()) + (hasFocusedTask ? 1 : 0); // Add the focused task to the beginning of both arrays if it exists. if (hasFocusedTask) { topRowIdArray.add(0, mFocusedTaskId); bottomRowIdArray.add(0, mFocusedTaskId); } // Fill in the shorter array with the ids from the longer one. for (int i = minSize; i < maxSize; i++) { if (i >= topRowIdArray.size()) { topRowIdArray.add(bottomRowIdArray.get(i)); } else { bottomRowIdArray.add(topRowIdArray.get(i)); } } // Add the clear all button to the end of both arrays topRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID); bottomRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID); mTopRowIds = topRowIdArray; mBottomRowIds = bottomRowIdArray; } /** * Returns the id of the next page in the grid or -1 for the clear all button. */ public int getNextGridPage(int currentPageTaskViewId, int delta, @TASK_NAV_DIRECTION int direction, boolean cycle) { boolean inTop = mTopRowIds.contains(currentPageTaskViewId); int index = inTop ? mTopRowIds.indexOf(currentPageTaskViewId) : mBottomRowIds.indexOf(currentPageTaskViewId); int maxSize = Math.max(mTopRowIds.size(), mBottomRowIds.size()); int nextIndex = index + delta; switch (direction) { case DIRECTION_UP: case DIRECTION_DOWN: { return inTop ? mBottomRowIds.get(index) : mTopRowIds.get(index); } case DIRECTION_LEFT: { int boundedIndex = cycle ? nextIndex % maxSize : Math.min(nextIndex, maxSize - 1); return inTop ? mTopRowIds.get(boundedIndex) : mBottomRowIds.get(boundedIndex); } case DIRECTION_RIGHT: { int boundedIndex = cycle ? (nextIndex < 0 ? maxSize - 1 : nextIndex) : Math.max( nextIndex, 0); boolean inOriginalTop = mOriginalTopRowIds.contains(currentPageTaskViewId); return inOriginalTop ? mTopRowIds.get(boundedIndex) : mBottomRowIds.get(boundedIndex); } case DIRECTION_TAB: { int boundedIndex = cycle ? nextIndex < 0 ? maxSize - 1 : nextIndex % maxSize : Math.min( nextIndex, maxSize - 1); if (delta >= 0) { return inTop && mTopRowIds.get(index) != mBottomRowIds.get(index) ? mBottomRowIds.get(index) : mTopRowIds.get(boundedIndex); } else { if (mTopRowIds.contains(currentPageTaskViewId)) { return mBottomRowIds.get(boundedIndex); } else { // Go up to top if there is task above return mTopRowIds.get(index) != mBottomRowIds.get(index) ? mTopRowIds.get(index) : mBottomRowIds.get(boundedIndex); } } } default: return currentPageTaskViewId; } } } quickstep/src/com/android/quickstep/views/RecentsView.java +52 −7 Original line number Diff line number Diff line Loading @@ -55,6 +55,11 @@ import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VAL import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; import static com.android.quickstep.util.LogUtils.splitFailureMessage; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_DOWN; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_LEFT; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_RIGHT; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_TAB; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_UP; import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA; import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET; Loading Loading @@ -196,6 +201,7 @@ import com.android.quickstep.util.SplitAnimationTimings; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskGridNavHelper; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TaskVisualsChangeListener; import com.android.quickstep.util.TransformParams; Loading Loading @@ -4065,12 +4071,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return anim; } private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) { private boolean snapToPageRelative(int delta, boolean cycle, @TaskGridNavHelper.TASK_NAV_DIRECTION int direction) { // Ignore grid page snap events while scroll animations are running, otherwise the next // page gets set before the animation finishes and can cause jumps. if (!mScroller.isFinished()) { return true; } int pageCount = getPageCount(); if (pageCount == 0) { return false; } final int newPageUnbound = getNextPage() + delta; if (!cycle && (newPageUnbound < 0 || newPageUnbound >= pageCount)) { final int newPageUnbound = getNextPageInternal(delta, direction, cycle); if (!cycle && (newPageUnbound < 0 || newPageUnbound > pageCount)) { return false; } snapToPage((newPageUnbound + pageCount) % pageCount); Loading @@ -4078,6 +4091,34 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return true; } private int getNextPageInternal(int delta, @TaskGridNavHelper.TASK_NAV_DIRECTION int direction, boolean cycle) { if (!showAsGrid()) { return getNextPage() + delta; } // Init task grid nav helper with top/bottom id arrays. TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(), getBottomRowIdArray(), mFocusedTaskViewId); // Get current page's task view ID. TaskView currentPageTaskView = getCurrentPageTaskView(); int currentPageTaskViewId; if (currentPageTaskView != null) { currentPageTaskViewId = currentPageTaskView.getTaskViewId(); } else if (mCurrentPage == indexOfChild(mClearAllButton)) { currentPageTaskViewId = TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID; } else { return INVALID_PAGE; } int nextGridPage = taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); return nextGridPage == TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID ? indexOfChild(mClearAllButton) : indexOfChild(getTaskViewFromTaskViewId(nextGridPage)); } private void runDismissAnimation(PendingAnimation pendingAnim) { AnimatorPlaybackController controller = pendingAnim.createPlaybackController(); controller.dispatchOnStart(); Loading Loading @@ -4119,12 +4160,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_TAB: return snapToPageRelative(getTaskViewCount(), event.isShiftPressed() ? -1 : 1, event.isAltPressed() /* cycle */); return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */, DIRECTION_TAB); case KeyEvent.KEYCODE_DPAD_RIGHT: return snapToPageRelative(getPageCount(), mIsRtl ? -1 : 1, false /* cycle */); return snapToPageRelative(mIsRtl ? -1 : 1, true /* cycle */, DIRECTION_RIGHT); case KeyEvent.KEYCODE_DPAD_LEFT: return snapToPageRelative(getPageCount(), mIsRtl ? 1 : -1, false /* cycle */); return snapToPageRelative(mIsRtl ? 1 : -1, true /* cycle */, DIRECTION_LEFT); case KeyEvent.KEYCODE_DPAD_UP: return snapToPageRelative(1, false /* cycle */, DIRECTION_UP); case KeyEvent.KEYCODE_DPAD_DOWN: return snapToPageRelative(1, false /* cycle */, DIRECTION_DOWN); case KeyEvent.KEYCODE_DEL: case KeyEvent.KEYCODE_FORWARD_DEL: dismissCurrentTask(); Loading quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java 0 → 100644 +510 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.quickstep.util; import static java.lang.annotation.RetentionPolicy.SOURCE; import androidx.annotation.IntDef; import com.android.launcher3.util.IntArray; import java.lang.annotation.Retention; /** * Helper class for navigating RecentsView grid tasks via arrow keys and tab. */ public class TaskGridNavHelper { public static final int CLEAR_ALL_PLACEHOLDER_ID = -1; public static final int INVALID_FOCUSED_TASK_ID = -1; public static final int DIRECTION_UP = 0; public static final int DIRECTION_DOWN = 1; public static final int DIRECTION_LEFT = 2; public static final int DIRECTION_RIGHT = 3; public static final int DIRECTION_TAB = 4; @Retention(SOURCE) @IntDef({DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_TAB}) public @interface TASK_NAV_DIRECTION {} private final IntArray mOriginalTopRowIds; private IntArray mTopRowIds; private IntArray mBottomRowIds; private final int mFocusedTaskId; public TaskGridNavHelper(IntArray topIds, IntArray bottomIds, int focusedTaskId) { mFocusedTaskId = focusedTaskId; mOriginalTopRowIds = topIds.clone(); generateTaskViewIdGrid(topIds, bottomIds); } private void generateTaskViewIdGrid(IntArray topRowIdArray, IntArray bottomRowIdArray) { boolean hasFocusedTask = mFocusedTaskId != INVALID_FOCUSED_TASK_ID; int maxSize = Math.max(topRowIdArray.size(), bottomRowIdArray.size()) + (hasFocusedTask ? 1 : 0); int minSize = Math.min(topRowIdArray.size(), bottomRowIdArray.size()) + (hasFocusedTask ? 1 : 0); // Add the focused task to the beginning of both arrays if it exists. if (hasFocusedTask) { topRowIdArray.add(0, mFocusedTaskId); bottomRowIdArray.add(0, mFocusedTaskId); } // Fill in the shorter array with the ids from the longer one. for (int i = minSize; i < maxSize; i++) { if (i >= topRowIdArray.size()) { topRowIdArray.add(bottomRowIdArray.get(i)); } else { bottomRowIdArray.add(topRowIdArray.get(i)); } } // Add the clear all button to the end of both arrays topRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID); bottomRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID); mTopRowIds = topRowIdArray; mBottomRowIds = bottomRowIdArray; } /** * Returns the id of the next page in the grid or -1 for the clear all button. */ public int getNextGridPage(int currentPageTaskViewId, int delta, @TASK_NAV_DIRECTION int direction, boolean cycle) { boolean inTop = mTopRowIds.contains(currentPageTaskViewId); int index = inTop ? mTopRowIds.indexOf(currentPageTaskViewId) : mBottomRowIds.indexOf(currentPageTaskViewId); int maxSize = Math.max(mTopRowIds.size(), mBottomRowIds.size()); int nextIndex = index + delta; switch (direction) { case DIRECTION_UP: case DIRECTION_DOWN: { return inTop ? mBottomRowIds.get(index) : mTopRowIds.get(index); } case DIRECTION_LEFT: { int boundedIndex = cycle ? nextIndex % maxSize : Math.min(nextIndex, maxSize - 1); return inTop ? mTopRowIds.get(boundedIndex) : mBottomRowIds.get(boundedIndex); } case DIRECTION_RIGHT: { int boundedIndex = cycle ? (nextIndex < 0 ? maxSize - 1 : nextIndex) : Math.max( nextIndex, 0); boolean inOriginalTop = mOriginalTopRowIds.contains(currentPageTaskViewId); return inOriginalTop ? mTopRowIds.get(boundedIndex) : mBottomRowIds.get(boundedIndex); } case DIRECTION_TAB: { int boundedIndex = cycle ? nextIndex < 0 ? maxSize - 1 : nextIndex % maxSize : Math.min( nextIndex, maxSize - 1); if (delta >= 0) { return inTop && mTopRowIds.get(index) != mBottomRowIds.get(index) ? mBottomRowIds.get(index) : mTopRowIds.get(boundedIndex); } else { if (mTopRowIds.contains(currentPageTaskViewId)) { return mBottomRowIds.get(boundedIndex); } else { // Go up to top if there is task above return mTopRowIds.get(index) != mBottomRowIds.get(index) ? mTopRowIds.get(index) : mBottomRowIds.get(boundedIndex); } } } default: return currentPageTaskViewId; } } }
quickstep/src/com/android/quickstep/views/RecentsView.java +52 −7 Original line number Diff line number Diff line Loading @@ -55,6 +55,11 @@ import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VAL import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; import static com.android.quickstep.util.LogUtils.splitFailureMessage; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_DOWN; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_LEFT; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_RIGHT; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_TAB; import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_UP; import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA; import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET; Loading Loading @@ -196,6 +201,7 @@ import com.android.quickstep.util.SplitAnimationTimings; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskGridNavHelper; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TaskVisualsChangeListener; import com.android.quickstep.util.TransformParams; Loading Loading @@ -4065,12 +4071,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return anim; } private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) { private boolean snapToPageRelative(int delta, boolean cycle, @TaskGridNavHelper.TASK_NAV_DIRECTION int direction) { // Ignore grid page snap events while scroll animations are running, otherwise the next // page gets set before the animation finishes and can cause jumps. if (!mScroller.isFinished()) { return true; } int pageCount = getPageCount(); if (pageCount == 0) { return false; } final int newPageUnbound = getNextPage() + delta; if (!cycle && (newPageUnbound < 0 || newPageUnbound >= pageCount)) { final int newPageUnbound = getNextPageInternal(delta, direction, cycle); if (!cycle && (newPageUnbound < 0 || newPageUnbound > pageCount)) { return false; } snapToPage((newPageUnbound + pageCount) % pageCount); Loading @@ -4078,6 +4091,34 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return true; } private int getNextPageInternal(int delta, @TaskGridNavHelper.TASK_NAV_DIRECTION int direction, boolean cycle) { if (!showAsGrid()) { return getNextPage() + delta; } // Init task grid nav helper with top/bottom id arrays. TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(), getBottomRowIdArray(), mFocusedTaskViewId); // Get current page's task view ID. TaskView currentPageTaskView = getCurrentPageTaskView(); int currentPageTaskViewId; if (currentPageTaskView != null) { currentPageTaskViewId = currentPageTaskView.getTaskViewId(); } else if (mCurrentPage == indexOfChild(mClearAllButton)) { currentPageTaskViewId = TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID; } else { return INVALID_PAGE; } int nextGridPage = taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); return nextGridPage == TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID ? indexOfChild(mClearAllButton) : indexOfChild(getTaskViewFromTaskViewId(nextGridPage)); } private void runDismissAnimation(PendingAnimation pendingAnim) { AnimatorPlaybackController controller = pendingAnim.createPlaybackController(); controller.dispatchOnStart(); Loading Loading @@ -4119,12 +4160,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_TAB: return snapToPageRelative(getTaskViewCount(), event.isShiftPressed() ? -1 : 1, event.isAltPressed() /* cycle */); return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */, DIRECTION_TAB); case KeyEvent.KEYCODE_DPAD_RIGHT: return snapToPageRelative(getPageCount(), mIsRtl ? -1 : 1, false /* cycle */); return snapToPageRelative(mIsRtl ? -1 : 1, true /* cycle */, DIRECTION_RIGHT); case KeyEvent.KEYCODE_DPAD_LEFT: return snapToPageRelative(getPageCount(), mIsRtl ? 1 : -1, false /* cycle */); return snapToPageRelative(mIsRtl ? 1 : -1, true /* cycle */, DIRECTION_LEFT); case KeyEvent.KEYCODE_DPAD_UP: return snapToPageRelative(1, false /* cycle */, DIRECTION_UP); case KeyEvent.KEYCODE_DPAD_DOWN: return snapToPageRelative(1, false /* cycle */, DIRECTION_DOWN); case KeyEvent.KEYCODE_DEL: case KeyEvent.KEYCODE_FORWARD_DEL: dismissCurrentTask(); Loading
quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java 0 → 100644 +510 −0 File added.Preview size limit exceeded, changes collapsed. Show changes