Add keyboard support to Grid-based Recents.
Test: Checked that pressing tab, shift + tab, alt + tab, alt + shift + tab work for task navigation in Recents on local sw600dp device. Also checked that Recents on phones work properly. Bug: 32101881 Change-Id: I3e4cd212d2900523ece30a85cae7fb73a9594efb
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#61FFFFFF" />
|
||||||
|
<corners android:radius="8dp"/>
|
||||||
|
</shape>
|
||||||
@@ -21,5 +21,6 @@
|
|||||||
<dimen name="recents_grid_padding_task_view">20dp</dimen>
|
<dimen name="recents_grid_padding_task_view">20dp</dimen>
|
||||||
<dimen name="recents_grid_task_view_header_height">44dp</dimen>
|
<dimen name="recents_grid_task_view_header_height">44dp</dimen>
|
||||||
<dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
|
<dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
|
||||||
|
<dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class RecentsActivityLaunchState {
|
|||||||
/**
|
/**
|
||||||
* Returns the task to focus given the current launch state.
|
* Returns the task to focus given the current launch state.
|
||||||
*/
|
*/
|
||||||
public int getInitialFocusTaskIndex(int numTasks) {
|
public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
|
||||||
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
|
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
|
||||||
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
|
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
|
||||||
if (launchedFromApp) {
|
if (launchedFromApp) {
|
||||||
@@ -66,6 +66,11 @@ public class RecentsActivityLaunchState {
|
|||||||
return numTasks - 1;
|
return numTasks - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useGridLayout) {
|
||||||
|
// If coming from another app to the grid layout, focus the front most task
|
||||||
|
return numTasks - 1;
|
||||||
|
}
|
||||||
|
|
||||||
// If coming from another app, focus the next task
|
// If coming from another app, focus the next task
|
||||||
return Math.max(0, numTasks - 2);
|
return Math.max(0, numTasks - 2);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
|
|||||||
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
|
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
|
||||||
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
|
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
|
||||||
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
|
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
|
||||||
|
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
|
||||||
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
|
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
|
||||||
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
|
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
|
||||||
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
|
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
|
||||||
@@ -93,6 +94,7 @@ import com.android.systemui.recents.model.Task;
|
|||||||
import com.android.systemui.recents.model.TaskStack;
|
import com.android.systemui.recents.model.TaskStack;
|
||||||
|
|
||||||
import com.android.systemui.recents.views.grid.GridTaskView;
|
import com.android.systemui.recents.views.grid.GridTaskView;
|
||||||
|
import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@@ -206,6 +208,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
private int mLastWidth;
|
private int mLastWidth;
|
||||||
private int mLastHeight;
|
private int mLastHeight;
|
||||||
|
|
||||||
|
// We keep track of the task view focused by user interaction and draw a frame around it in the
|
||||||
|
// grid layout.
|
||||||
|
private TaskViewFocusFrame mTaskViewFocusFrame;
|
||||||
|
|
||||||
// A convenience update listener to request updating clipping of tasks
|
// A convenience update listener to request updating clipping of tasks
|
||||||
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
|
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
|
||||||
new ValueAnimator.AnimatorUpdateListener() {
|
new ValueAnimator.AnimatorUpdateListener() {
|
||||||
@@ -265,6 +271,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
|
mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
|
||||||
mDisplayRect = ssp.getDisplayRect();
|
mDisplayRect = ssp.getDisplayRect();
|
||||||
|
|
||||||
|
// Create a frame to draw around the focused task view
|
||||||
|
if (Recents.getConfiguration().isGridEnabled) {
|
||||||
|
mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
|
||||||
|
mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
|
||||||
|
addView(mTaskViewFocusFrame);
|
||||||
|
getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
|
||||||
|
}
|
||||||
|
|
||||||
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
|
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
|
||||||
R.integer.recents_task_bar_dismiss_delay_seconds);
|
R.integer.recents_task_bar_dismiss_delay_seconds);
|
||||||
mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
|
mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
|
||||||
@@ -878,7 +892,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
*
|
*
|
||||||
* @return whether or not the stack will scroll as a part of this focus change
|
* @return whether or not the stack will scroll as a part of this focus change
|
||||||
*/
|
*/
|
||||||
private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
|
public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
|
||||||
final boolean requestViewFocus) {
|
final boolean requestViewFocus) {
|
||||||
return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
|
return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
|
||||||
}
|
}
|
||||||
@@ -888,7 +902,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
*
|
*
|
||||||
* @return whether or not the stack will scroll as a part of this focus change
|
* @return whether or not the stack will scroll as a part of this focus change
|
||||||
*/
|
*/
|
||||||
private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
|
public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
|
||||||
boolean requestViewFocus, int timerIndicatorDuration) {
|
boolean requestViewFocus, int timerIndicatorDuration) {
|
||||||
// Find the next task to focus
|
// Find the next task to focus
|
||||||
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
|
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
|
||||||
@@ -940,6 +954,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
newFocusedTaskView.setFocusedState(true, requestViewFocus);
|
newFocusedTaskView.setFocusedState(true, requestViewFocus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Any time a task view gets the focus, we move the focus frame around it.
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return willScroll;
|
return willScroll;
|
||||||
}
|
}
|
||||||
@@ -1005,20 +1023,28 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
float stackScroll = mStackScroller.getStackScroll();
|
float stackScroll = mStackScroller.getStackScroll();
|
||||||
ArrayList<Task> tasks = mStack.getStackTasks();
|
ArrayList<Task> tasks = mStack.getStackTasks();
|
||||||
int taskCount = tasks.size();
|
int taskCount = tasks.size();
|
||||||
if (forward) {
|
if (useGridLayout()) {
|
||||||
// Walk backwards and focus the next task smaller than the current stack scroll
|
// For the grid layout, we directly set focus to the most recently used task
|
||||||
for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
|
// no matter we're moving forwards or backwards.
|
||||||
float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
|
newIndex = taskCount - 1;
|
||||||
if (Float.compare(taskP, stackScroll) <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Walk forwards and focus the next task larger than the current stack scroll
|
// For the grid layout we pick a proper task to focus, according to the current
|
||||||
for (newIndex = 0; newIndex < taskCount; newIndex++) {
|
// stack scroll.
|
||||||
float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
|
if (forward) {
|
||||||
if (Float.compare(taskP, stackScroll) >= 0) {
|
// Walk backwards and focus the next task smaller than the current stack scroll
|
||||||
break;
|
for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
|
||||||
|
float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
|
||||||
|
if (Float.compare(taskP, stackScroll) <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Walk forwards and focus the next task larger than the current stack scroll
|
||||||
|
for (newIndex = 0; newIndex < taskCount; newIndex++) {
|
||||||
|
float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
|
||||||
|
if (Float.compare(taskP, stackScroll) >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1037,20 +1063,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
/**
|
/**
|
||||||
* Resets the focused task.
|
* Resets the focused task.
|
||||||
*/
|
*/
|
||||||
void resetFocusedTask(Task task) {
|
public void resetFocusedTask(Task task) {
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
TaskView tv = getChildViewForTask(task);
|
TaskView tv = getChildViewForTask(task);
|
||||||
if (tv != null) {
|
if (tv != null) {
|
||||||
tv.setFocusedState(false, false /* requestViewFocus */);
|
tv.setFocusedState(false, false /* requestViewFocus */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.moveGridTaskViewFocus(null);
|
||||||
|
}
|
||||||
mFocusedTask = null;
|
mFocusedTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the focused task.
|
* Returns the focused task.
|
||||||
*/
|
*/
|
||||||
Task getFocusedTask() {
|
public Task getFocusedTask() {
|
||||||
return mFocusedTask;
|
return mFocusedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,6 +1282,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
for (int i = 0; i < taskViewCount; i++) {
|
for (int i = 0; i < taskViewCount; i++) {
|
||||||
measureTaskView(mTmpTaskViews.get(i));
|
measureTaskView(mTmpTaskViews.get(i));
|
||||||
}
|
}
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.measure();
|
||||||
|
}
|
||||||
|
|
||||||
setMeasuredDimension(width, height);
|
setMeasuredDimension(width, height);
|
||||||
mLastWidth = width;
|
mLastWidth = width;
|
||||||
@@ -1287,6 +1319,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
for (int i = 0; i < taskViewCount; i++) {
|
for (int i = 0; i < taskViewCount; i++) {
|
||||||
layoutTaskView(changed, mTmpTaskViews.get(i));
|
layoutTaskView(changed, mTmpTaskViews.get(i));
|
||||||
}
|
}
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.layout();
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
if (mStackScroller.isScrollOutOfBounds()) {
|
if (mStackScroller.isScrollOutOfBounds()) {
|
||||||
@@ -1339,10 +1374,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
// until after the enter-animation
|
// until after the enter-animation
|
||||||
RecentsConfiguration config = Recents.getConfiguration();
|
RecentsConfiguration config = Recents.getConfiguration();
|
||||||
RecentsActivityLaunchState launchState = config.getLaunchState();
|
RecentsActivityLaunchState launchState = config.getLaunchState();
|
||||||
int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
|
|
||||||
if (focusedTaskIndex != -1) {
|
// We set the initial focused task view iff the following conditions are satisfied:
|
||||||
setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
|
// 1. Recents is showing task views in stack layout.
|
||||||
false /* requestViewFocus */);
|
// 2. Recents is launched with ALT + TAB.
|
||||||
|
boolean setFocusOnFirstLayout = !useGridLayout() ||
|
||||||
|
Recents.getConfiguration().getLaunchState().launchedWithAltTab;
|
||||||
|
if (setFocusOnFirstLayout) {
|
||||||
|
int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
|
||||||
|
useGridLayout());
|
||||||
|
if (focusedTaskIndex != -1) {
|
||||||
|
setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
|
||||||
|
false /* requestViewFocus */);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateStackActionButtonVisibility();
|
updateStackActionButtonVisibility();
|
||||||
}
|
}
|
||||||
@@ -1443,6 +1487,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
// Remove the task from the ignored set
|
// Remove the task from the ignored set
|
||||||
removeIgnoreTask(removedTask);
|
removeIgnoreTask(removedTask);
|
||||||
|
|
||||||
|
// Resize the grid layout task view focus frame
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.resize();
|
||||||
|
}
|
||||||
|
|
||||||
// If requested, relayout with the given animation
|
// If requested, relayout with the given animation
|
||||||
if (animation != null) {
|
if (animation != null) {
|
||||||
updateLayoutAlgorithm(true /* boundScroll */);
|
updateLayoutAlgorithm(true /* boundScroll */);
|
||||||
@@ -1740,10 +1789,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
|
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
|
||||||
animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
|
animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
|
||||||
Interpolators.FAST_OUT_SLOW_IN));
|
Interpolators.FAST_OUT_SLOW_IN));
|
||||||
|
|
||||||
|
// Dismiss the grid task view focus frame
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.moveGridTaskViewFocus(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void onBusEvent(DismissFocusedTaskViewEvent event) {
|
public final void onBusEvent(DismissFocusedTaskViewEvent event) {
|
||||||
if (mFocusedTask != null) {
|
if (mFocusedTask != null) {
|
||||||
|
if (mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.moveGridTaskViewFocus(null);
|
||||||
|
}
|
||||||
TaskView tv = getChildViewForTask(mFocusedTask);
|
TaskView tv = getChildViewForTask(mFocusedTask);
|
||||||
if (tv != null) {
|
if (tv != null) {
|
||||||
tv.dismissTask();
|
tv.dismissTask();
|
||||||
@@ -2073,6 +2130,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
|||||||
mResetToInitialStateWhenResized = true;
|
mResetToInitialStateWhenResized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
|
||||||
|
if (!event.visible && mTaskViewFocusFrame != null) {
|
||||||
|
mTaskViewFocusFrame.moveGridTaskViewFocus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void reloadOnConfigurationChange() {
|
public void reloadOnConfigurationChange() {
|
||||||
mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
|
mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
|
||||||
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
|
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
|
||||||
|
|||||||
@@ -342,8 +342,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
|||||||
mSv.invalidate();
|
mSv.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the focused task after the user has scrolled
|
// Reset the focused task after the user has scrolled, but we have no scrolling
|
||||||
if (!mSv.mTouchExplorationEnabled) {
|
// in grid layout and therefore we don't want to reset the focus there.
|
||||||
|
if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
|
||||||
mSv.resetFocusedTask(mSv.getFocusedTask());
|
mSv.resetFocusedTask(mSv.getFocusedTask());
|
||||||
}
|
}
|
||||||
} else if (mActiveTaskView == null) {
|
} else if (mActiveTaskView == null) {
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ public class TaskGridLayoutAlgorithm {
|
|||||||
private float mAppAspectRatio;
|
private float mAppAspectRatio;
|
||||||
private Rect mSystemInsets = new Rect();
|
private Rect mSystemInsets = new Rect();
|
||||||
|
|
||||||
|
/** The thickness of the focused task view frame. */
|
||||||
|
private int mFocusedFrameThickness;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the amount of tasks is determined, the size and position of every task view can be
|
* When the amount of tasks is determined, the size and position of every task view can be
|
||||||
* decided. Each instance of TaskGridRectInfo store the task view information for a certain
|
* decided. Each instance of TaskGridRectInfo store the task view information for a certain
|
||||||
@@ -137,6 +140,9 @@ public class TaskGridLayoutAlgorithm {
|
|||||||
public void reloadOnConfigurationChange(Context context) {
|
public void reloadOnConfigurationChange(Context context) {
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
|
mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
|
||||||
|
mFocusedFrameThickness = res.getDimensionPixelSize(
|
||||||
|
R.dimen.recents_grid_task_view_focused_frame_thickness);
|
||||||
|
|
||||||
mTaskGridRect = new Rect();
|
mTaskGridRect = new Rect();
|
||||||
mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
|
mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
|
||||||
|
|
||||||
@@ -223,7 +229,18 @@ public class TaskGridLayoutAlgorithm {
|
|||||||
return buttonRect;
|
return buttonRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateTaskGridRect(int taskCount) {
|
||||||
|
if (taskCount > 0) {
|
||||||
|
TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
|
||||||
|
mTaskGridRect.set(gridInfo.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Rect getTaskGridRect() {
|
public Rect getTaskGridRect() {
|
||||||
return mTaskGridRect;
|
return mTaskGridRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getFocusFrameThickness() {
|
||||||
|
return mFocusedFrameThickness;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.views.grid;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.recents.model.TaskStack;
|
||||||
|
import com.android.systemui.recents.views.TaskStackView;
|
||||||
|
|
||||||
|
public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
|
||||||
|
|
||||||
|
private TaskStackView mSv;
|
||||||
|
private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
|
||||||
|
public TaskViewFocusFrame(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskViewFocusFrame(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
this(context, attrs, defStyleAttr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
|
||||||
|
int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
setBackground(mContext.getDrawable(
|
||||||
|
R.drawable.recents_grid_task_view_focus_frame_background));
|
||||||
|
setFocusable(false);
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskViewFocusFrame(Context context, TaskStackView stackView,
|
||||||
|
TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
|
||||||
|
this(context);
|
||||||
|
mSv = stackView;
|
||||||
|
mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measure the width and height of the focus frame according to the current grid task view size.
|
||||||
|
*/
|
||||||
|
public void measure() {
|
||||||
|
int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
|
||||||
|
Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
|
||||||
|
measure(
|
||||||
|
MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout the focus frame with its size.
|
||||||
|
*/
|
||||||
|
public void layout() {
|
||||||
|
layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current size of grid task view and the focus frame.
|
||||||
|
*/
|
||||||
|
public void resize() {
|
||||||
|
if (mSv.useGridLayout()) {
|
||||||
|
mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
|
||||||
|
measure();
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the task view focus frame to surround the newly focused view. If it's {@code null} or
|
||||||
|
* it's not an instance of GridTaskView, we hide the focus frame.
|
||||||
|
* @param newFocus The newly focused view.
|
||||||
|
*/
|
||||||
|
public void moveGridTaskViewFocus(View newFocus) {
|
||||||
|
if (mSv.useGridLayout()) {
|
||||||
|
// The frame only shows up in the grid layout. It shouldn't show up in the stack
|
||||||
|
// layout including when we're in the split screen.
|
||||||
|
if (newFocus instanceof GridTaskView) {
|
||||||
|
// If the focus goes to a GridTaskView, we show the frame and layout it.
|
||||||
|
int[] location = new int[2];
|
||||||
|
newFocus.getLocationInWindow(location);
|
||||||
|
int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
|
||||||
|
setTranslationX(location[0] - thickness);
|
||||||
|
setTranslationY(location[1] - thickness);
|
||||||
|
show();
|
||||||
|
} else {
|
||||||
|
// If focus goes to other views, we hide the frame.
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
|
||||||
|
if (!mSv.useGridLayout()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newFocus == null) {
|
||||||
|
// We're going to touch mode, unset the focus.
|
||||||
|
moveGridTaskViewFocus(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oldFocus == null) {
|
||||||
|
// We're returning from touch mode, set the focus to the previously focused task.
|
||||||
|
final TaskStack stack = mSv.getStack();
|
||||||
|
final int taskCount = stack.getTaskCount();
|
||||||
|
final int k = stack.indexOfStackTask(mSv.getFocusedTask());
|
||||||
|
final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
|
||||||
|
mSv.setFocusedTask(taskIndexToFocus, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show() {
|
||||||
|
setAlpha(1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hide() {
|
||||||
|
setAlpha(0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user