Add a grid-based layout algorithm for Recents.
This commit adds a layout algorithm for Recents so that task views are layouted in a grid mode, to take full use of the screen space. Change-Id: Ia0e388ea2e8a6ae348ed002a830212e8fea546fc
This commit is contained in:
23
packages/SystemUI/res/values/dimens_grid.xml
Normal file
23
packages/SystemUI/res/values/dimens_grid.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="recents_grid_padding_left_right">32dp</dimen>
|
||||
<dimen name="recents_grid_padding_top_bottom">84dp</dimen>
|
||||
<dimen name="recents_grid_padding_task_view">20dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -157,7 +157,7 @@ public class TaskStackAnimationHelper {
|
||||
|
||||
// Get the current transform for the task, which will be used to position it offscreen
|
||||
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
|
||||
null);
|
||||
null, mStackView.useGridLayout());
|
||||
|
||||
if (hideTask) {
|
||||
tv.setVisibility(View.INVISIBLE);
|
||||
@@ -230,7 +230,7 @@ public class TaskStackAnimationHelper {
|
||||
// Get the current transform for the task, which will be updated to the final transform
|
||||
// to animate to depending on how recents was invoked
|
||||
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
|
||||
null);
|
||||
null, mStackView.useGridLayout());
|
||||
|
||||
if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
|
||||
if (task.isLaunchTarget) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
|
||||
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -326,7 +326,7 @@ public class TaskStackLayoutAlgorithm {
|
||||
@ViewDebug.ExportedProperty(category="recents")
|
||||
int mMinTranslationZ;
|
||||
@ViewDebug.ExportedProperty(category="recents")
|
||||
int mMaxTranslationZ;
|
||||
public int mMaxTranslationZ;
|
||||
|
||||
// Optimization, allows for quick lookup of task -> index
|
||||
private SparseIntArray mTaskIndexMap = new SparseIntArray();
|
||||
@@ -334,6 +334,7 @@ public class TaskStackLayoutAlgorithm {
|
||||
|
||||
// The freeform workspace layout
|
||||
FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
|
||||
TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
|
||||
|
||||
// The transform to place TaskViews at the front and back of the stack respectively
|
||||
TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
|
||||
@@ -344,6 +345,7 @@ public class TaskStackLayoutAlgorithm {
|
||||
mContext = context;
|
||||
mCb = cb;
|
||||
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
|
||||
mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
|
||||
reloadOnConfigurationChange(context);
|
||||
}
|
||||
|
||||
@@ -377,6 +379,7 @@ public class TaskStackLayoutAlgorithm {
|
||||
R.dimen.recents_layout_initial_bottom_offset_tablet,
|
||||
R.dimen.recents_layout_initial_bottom_offset_tablet);
|
||||
mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
|
||||
mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
|
||||
mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
|
||||
mBaseTopMargin = getDimensionForDevice(context,
|
||||
R.dimen.recents_layout_top_margin_phone,
|
||||
@@ -470,6 +473,9 @@ public class TaskStackLayoutAlgorithm {
|
||||
|
||||
updateFrontBackTransforms();
|
||||
}
|
||||
|
||||
// Initialize the grid layout
|
||||
mTaskGridLayoutAlgorithm.initialize(displayRect, windowRect);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -825,24 +831,30 @@ public class TaskStackLayoutAlgorithm {
|
||||
* is what the view is measured and laid out with.
|
||||
*/
|
||||
public TaskViewTransform getStackTransform(Task task, float stackScroll,
|
||||
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
|
||||
TaskViewTransform transformOut, TaskViewTransform frontTransform,
|
||||
boolean useGridLayout) {
|
||||
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
|
||||
false /* forceUpdate */, false /* ignoreTaskOverrides */);
|
||||
false /* forceUpdate */, false /* ignoreTaskOverrides */, useGridLayout);
|
||||
}
|
||||
|
||||
public TaskViewTransform getStackTransform(Task task, float stackScroll,
|
||||
TaskViewTransform transformOut, TaskViewTransform frontTransform,
|
||||
boolean ignoreTaskOverrides) {
|
||||
boolean ignoreTaskOverrides, boolean useGridLayout) {
|
||||
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
|
||||
false /* forceUpdate */, ignoreTaskOverrides);
|
||||
false /* forceUpdate */, ignoreTaskOverrides, useGridLayout);
|
||||
}
|
||||
|
||||
public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
|
||||
TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
|
||||
boolean ignoreTaskOverrides) {
|
||||
boolean ignoreTaskOverrides, boolean useGridLayout) {
|
||||
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
|
||||
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
|
||||
return transformOut;
|
||||
} else if (useGridLayout) {
|
||||
int taskIndex = mTaskIndexMap.get(task.key.id);
|
||||
int taskCount = mTaskIndexMap.size();
|
||||
mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
|
||||
return transformOut;
|
||||
} else {
|
||||
// Return early if we have an invalid index
|
||||
int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
|
||||
@@ -867,7 +879,7 @@ public class TaskStackLayoutAlgorithm {
|
||||
Rect windowOverrideRect) {
|
||||
TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
|
||||
transformOut, frontTransform, true /* forceUpdate */,
|
||||
false /* ignoreTaskOverrides */);
|
||||
false /* ignoreTaskOverrides */, false /* useGridLayout */);
|
||||
return transformToScreenCoordinates(transform, windowOverrideRect);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
private int mTaskCornerRadiusPx;
|
||||
private int mDividerSize;
|
||||
private int mStartTimerIndicatorDuration;
|
||||
private boolean mDraggingOverDockState;
|
||||
|
||||
@ViewDebug.ExportedProperty(category="recents")
|
||||
private boolean mTaskViewsClipDirty = true;
|
||||
@@ -500,13 +501,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
// Calculate the current and (if necessary) the target transform for the task
|
||||
transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
|
||||
taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
|
||||
taskTransforms.get(i), frontTransform, ignoreTaskOverrides, useGridLayout());
|
||||
if (useTargetStackScroll && !transform.visible) {
|
||||
// If we have a target stack scroll and the task is not currently visible, then we
|
||||
// just update the transform at the new scroll
|
||||
// TODO: Optimize this
|
||||
transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
|
||||
targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
|
||||
transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
|
||||
new TaskViewTransform(), frontTransformAtTarget, useGridLayout());
|
||||
if (transformAtTarget.visible) {
|
||||
transform.copyFrom(transformAtTarget);
|
||||
}
|
||||
@@ -737,7 +738,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
} else {
|
||||
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
|
||||
focusState, transform, null, true /* forceUpdate */,
|
||||
false /* ignoreTaskOverrides */);
|
||||
false /* ignoreTaskOverrides */, useGridLayout());
|
||||
}
|
||||
transform.visible = true;
|
||||
}
|
||||
@@ -754,7 +755,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
Task task = tasks.get(i);
|
||||
TaskViewTransform transform = transformsOut.get(i);
|
||||
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
|
||||
true /* forceUpdate */, ignoreTaskOverrides);
|
||||
true /* forceUpdate */, ignoreTaskOverrides, useGridLayout());
|
||||
transform.visible = true;
|
||||
}
|
||||
}
|
||||
@@ -1840,7 +1841,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
// Enlarge the dragged view slightly
|
||||
float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
|
||||
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
|
||||
mTmpTransform, null);
|
||||
mTmpTransform, null, useGridLayout());
|
||||
mTmpTransform.scale = finalScale;
|
||||
mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
|
||||
mTmpTransform.dimAlpha = 0f;
|
||||
@@ -1861,6 +1862,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
Interpolators.FAST_OUT_SLOW_IN);
|
||||
boolean ignoreTaskOverrides = false;
|
||||
if (event.dropTarget instanceof TaskStack.DockState) {
|
||||
mDraggingOverDockState = true;
|
||||
// Calculate the new task stack bounds that matches the window size that Recents will
|
||||
// have after the drop
|
||||
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
|
||||
@@ -1880,6 +1882,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
updateLayoutAlgorithm(true /* boundScroll */);
|
||||
ignoreTaskOverrides = true;
|
||||
} else {
|
||||
mDraggingOverDockState = false;
|
||||
// Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
|
||||
// task view, so add it back to the ignore set after updating the layout
|
||||
removeIgnoreTask(event.task);
|
||||
@@ -1890,6 +1893,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
|
||||
public final void onBusEvent(final DragEndEvent event) {
|
||||
mDraggingOverDockState = false;
|
||||
// We don't handle drops on the dock regions
|
||||
if (event.dropTarget instanceof TaskStack.DockState) {
|
||||
// However, we do need to reset the overrides, since the last state of this task stack
|
||||
@@ -2128,6 +2132,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we should use the grid layout.
|
||||
* We use the grid layout for Recents iff all the following is true:
|
||||
* 1. Grid-mode is enabled.
|
||||
* 2. The activity is not in multi-window mode.
|
||||
* 3. The user is not dragging a task view over the dock state.
|
||||
* @return True if we should use the grid layout.
|
||||
*/
|
||||
public boolean useGridLayout() {
|
||||
return Recents.getConfiguration().isGridEnabled
|
||||
&& !((RecentsActivity) mContext).isInMultiWindowMode()
|
||||
&& !mDraggingOverDockState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads current system flags related to accessibility and screen pinning.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
|
||||
import com.android.systemui.recents.views.TaskViewTransform;
|
||||
|
||||
public class TaskGridLayoutAlgorithm {
|
||||
|
||||
private int mPaddingLeftRight;
|
||||
private int mPaddingTopBottom;
|
||||
private int mPaddingTaskView;
|
||||
|
||||
private Rect mDisplayRect;
|
||||
private Rect mWindowRect;
|
||||
|
||||
private Rect mTaskGridRect;
|
||||
|
||||
public TaskGridLayoutAlgorithm(Context context) {
|
||||
reloadOnConfigurationChange(context);
|
||||
}
|
||||
|
||||
public void reloadOnConfigurationChange(Context context) {
|
||||
Resources res = context.getResources();
|
||||
mPaddingLeftRight = res.getDimensionPixelSize(R.dimen.recents_grid_padding_left_right);
|
||||
mPaddingTopBottom = res.getDimensionPixelSize(R.dimen.recents_grid_padding_top_bottom);
|
||||
mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
|
||||
|
||||
mTaskGridRect = new Rect();
|
||||
}
|
||||
|
||||
public TaskViewTransform getTransform(int taskIndex, int taskAmount,
|
||||
TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
|
||||
|
||||
int taskPerLine = taskAmount < 2 ? 1 : (
|
||||
taskAmount < 5 ? 2 : 3);
|
||||
|
||||
int taskWidth = (mDisplayRect.width() - mPaddingLeftRight * 2
|
||||
- mPaddingTaskView * (taskPerLine - 1)) / taskPerLine;
|
||||
int taskHeight = taskWidth * mDisplayRect.height() / mDisplayRect.width();
|
||||
mTaskGridRect.set(0, 0, taskWidth, taskHeight);
|
||||
|
||||
int xIndex = taskIndex % taskPerLine;
|
||||
int yIndex = taskIndex / taskPerLine;
|
||||
int x = mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
|
||||
int y = mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
|
||||
float z = stackLayout.mMaxTranslationZ;
|
||||
|
||||
float dimAlpha = 0f;
|
||||
float viewOutlineAlpha = 0f;
|
||||
|
||||
// Fill out the transform
|
||||
transformOut.scale = 1f;
|
||||
transformOut.alpha = 1f;
|
||||
transformOut.translationZ = z;
|
||||
transformOut.dimAlpha = dimAlpha;
|
||||
transformOut.viewOutlineAlpha = viewOutlineAlpha;
|
||||
transformOut.rect.set(mTaskGridRect);
|
||||
transformOut.rect.offset(x, y);
|
||||
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
|
||||
transformOut.visible = true;
|
||||
return transformOut;
|
||||
}
|
||||
|
||||
public void initialize(Rect displayRect, Rect windowRect) {
|
||||
mDisplayRect = displayRect;
|
||||
mWindowRect = windowRect;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user