Add arrow key navigation support to Grid-based Recents.

Test: Checked that arrow key navigation works in Recents on local
sw600dp devices. Also checked that Recents works properly on phones.
Bug: 32101881

Change-Id: I8ece3fc4c9ae82916efcbbde72d40805e43cc70d
This commit is contained in:
Jiaquan He
2017-01-11 14:47:00 -08:00
parent 2a147464f6
commit c0e1813944
4 changed files with 126 additions and 9 deletions

View File

@@ -74,6 +74,8 @@ import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -590,13 +592,12 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
return true;
}
case KeyEvent.KEYCODE_DPAD_UP: {
EventBus.getDefault().send(
new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT: {
final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
return true;
}
case KeyEvent.KEYCODE_DEL:

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2017 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.systemui.recents.events.ui.focus;
import android.view.KeyEvent;
import com.android.systemui.recents.events.EventBus;
/**
* Navigates the task view by arrow keys.
*/
public class NavigateTaskViewEvent extends EventBus.Event {
public enum Direction {
UNDEFINED, UP, DOWN, LEFT, RIGHT;
}
public Direction direction;
public NavigateTaskViewEvent(Direction direction) {
this.direction = direction;
}
public static Direction getDirectionFromKeyCode(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
return Direction.UP;
case KeyEvent.KEYCODE_DPAD_DOWN:
return Direction.DOWN;
case KeyEvent.KEYCODE_DPAD_LEFT:
return Direction.LEFT;
case KeyEvent.KEYCODE_DPAD_RIGHT:
return Direction.RIGHT;
default:
return Direction.UNDEFINED;
}
}
}

View File

@@ -87,6 +87,7 @@ import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropT
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -1869,6 +1870,26 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
}
public final void onBusEvent(NavigateTaskViewEvent event) {
if (useGridLayout()) {
final int taskCount = mStack.getTaskCount();
final int currentIndex = mStack.indexOfStackTask(getFocusedTask());
final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
currentIndex, event.direction);
setFocusedTask(nextIndex, false, true);
} else {
switch (event.direction) {
case UP:
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
break;
case DOWN:
EventBus.getDefault().send(
new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
break;
}
}
}
public final void onBusEvent(UserInteractionEvent event) {
// Poke the doze trigger on user interaction
mUIDozeTrigger.poke();

View File

@@ -23,6 +23,8 @@ import android.graphics.Rect;
import android.view.WindowManager;
import com.android.systemui.R;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskViewTransform;
@@ -63,6 +65,8 @@ public class TaskGridLayoutAlgorithm {
Rect size;
int[] xOffsets;
int[] yOffsets;
int tasksPerLine;
int lines;
TaskGridRectInfo(int taskCount) {
size = new Rect();
@@ -71,10 +75,10 @@ public class TaskGridLayoutAlgorithm {
int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
int tasksPerLine = layoutTaskCount < 2 ? 1 : (
tasksPerLine = layoutTaskCount < 2 ? 1 : (
layoutTaskCount < 5 ? 2 : (
layoutTaskCount < 7 ? 3 : 4));
int lines = layoutTaskCount < 3 ? 1 : 2;
lines = layoutTaskCount < 3 ? 1 : 2;
// A couple of special cases.
boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
@@ -200,6 +204,48 @@ public class TaskGridLayoutAlgorithm {
return transformOut;
}
/**
* Return the proper task index to focus for arrow key navigation.
* @param taskCount The amount of tasks.
* @param currentFocusedIndex The index of the currently focused task.
* @param direction The direction we're navigating.
* @return The index of the task that should get the focus.
*/
public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
return -1;
}
if (currentFocusedIndex == -1) {
return 0;
}
int newIndex = currentFocusedIndex;
final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
switch (direction) {
case UP:
newIndex += gridInfo.tasksPerLine;
newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
break;
case DOWN:
newIndex -= gridInfo.tasksPerLine;
newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
break;
case LEFT:
newIndex++;
final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
break;
case RIGHT:
newIndex--;
int rightMostIndex =
(taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
break;
}
return newIndex;
}
public void initialize(Rect windowRect) {
mWindowRect = windowRect;
// Define paddings in terms of percentage of the total area.