Merge "DO NOT MERGE - Port 2D recents implementation from master to nyc-mr2" into nyc-mr2-dev

This commit is contained in:
TreeHugger Robot
2016-12-20 21:50:13 +00:00
committed by Android (Google) Code Review
21 changed files with 609 additions and 147 deletions

View File

@@ -0,0 +1,35 @@
<?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.
-->
<com.android.systemui.recents.views.grid.GridTaskView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true">
<com.android.systemui.recents.views.TaskViewThumbnail
android:id="@+id/task_view_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/recents_task_view_header" />
<!-- TODO: Move this into a view stub -->
<include layout="@layout/recents_task_view_lock_to_app"/>
<!-- The incompatible app toast -->
<include layout="@layout/recents_task_view_incompatible_app_toast"/>
</com.android.systemui.recents.views.grid.GridTaskView>

View File

@@ -26,35 +26,10 @@
<include layout="@layout/recents_task_view_header" /> <include layout="@layout/recents_task_view_header" />
<!-- TODO: Move this into a view stub --> <!-- TODO: Move this into a view stub -->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout <include layout="@layout/recents_task_view_lock_to_app"/>
android:id="@+id/lock_to_app_fab"
android:layout_width="@dimen/recents_lock_to_app_size"
android:layout_height="@dimen/recents_lock_to_app_size"
android:layout_gravity="bottom|end"
android:layout_marginEnd="15dp"
android:layout_marginBottom="15dp"
android:translationZ="4dp"
android:contentDescription="@string/recents_lock_to_app_button_label"
android:background="@drawable/recents_lock_to_task_button_bg"
android:visibility="invisible"
android:alpha="0">
<ImageView
android:layout_width="@dimen/recents_lock_to_app_icon_size"
android:layout_height="@dimen/recents_lock_to_app_icon_size"
android:layout_gravity="center"
android:src="@drawable/recents_lock_to_app_pin" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
<!-- The incompatible app toast --> <!-- The incompatible app toast -->
<ViewStub android:id="@+id/incompatible_app_toast_stub" <include layout="@layout/recents_task_view_incompatible_app_toast"/>
android:inflatedId="@+id/incompatible_app_toast"
android:layout="@*android:layout/transient_notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="48dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" />
</com.android.systemui.recents.views.TaskView> </com.android.systemui.recents.views.TaskView>

View File

@@ -0,0 +1,26 @@
<?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.
-->
<ViewStub
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/incompatible_app_toast_stub"
android:inflatedId="@+id/incompatible_app_toast"
android:layout="@*android:layout/transient_notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="48dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" />

View File

@@ -0,0 +1,34 @@
<?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.
-->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/lock_to_app_fab"
android:layout_width="@dimen/recents_lock_to_app_size"
android:layout_height="@dimen/recents_lock_to_app_size"
android:layout_gravity="bottom|end"
android:layout_marginEnd="15dp"
android:layout_marginBottom="15dp"
android:translationZ="4dp"
android:contentDescription="@string/recents_lock_to_app_button_label"
android:background="@drawable/recents_lock_to_task_button_bg"
android:visibility="invisible"
android:alpha="0">
<ImageView
android:layout_width="@dimen/recents_lock_to_app_icon_size"
android:layout_height="@dimen/recents_lock_to_app_icon_size"
android:layout_gravity="center"
android:src="@drawable/recents_lock_to_app_pin" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>

View File

@@ -0,0 +1,25 @@
<?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">150dp</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_button_padding">8dp</dimen>
</resources>

View File

@@ -207,8 +207,6 @@ public class Recents extends SystemUI
getSystemService(Context.UI_MODE_SERVICE); getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
mImpl = new RecentsTvImpl(mContext); mImpl = new RecentsTvImpl(mContext);
} else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) {
mImpl = new RecentsGridImpl(mContext);
} else { } else {
mImpl = new RecentsImpl(mContext); mImpl = new RecentsImpl(mContext);
} }

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.SystemProperties;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -58,6 +59,10 @@ public class RecentsConfiguration {
public boolean fakeShadows; public boolean fakeShadows;
public int svelteLevel; public int svelteLevel;
// Whether this product supports Grid-based Recents. If this is field is set to true, then
// Recents will layout task views in a grid mode when there's enough space in the screen.
public boolean isGridEnabled;
public RecentsConfiguration(Context context) { public RecentsConfiguration(Context context) {
// Load only resources that can not change after the first load either through developer // Load only resources that can not change after the first load either through developer
// settings or via multi window // settings or via multi window
@@ -66,6 +71,7 @@ public class RecentsConfiguration {
Resources res = appContext.getResources(); Resources res = appContext.getResources();
fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows); fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
svelteLevel = res.getInteger(R.integer.recents_svelte_level); svelteLevel = res.getInteger(R.integer.recents_svelte_level);
isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
float screenDensity = context.getResources().getDisplayMetrics().density; float screenDensity = context.getResources().getDisplayMetrics().density;
smallestWidth = ssp.getDeviceSmallestWidth(); smallestWidth = ssp.getDeviceSmallestWidth();

View File

@@ -180,10 +180,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
ssp.registerTaskStackListener(mTaskStackListener); ssp.registerTaskStackListener(mTaskStackListener);
// Initialize the static configuration resources // Initialize the static configuration resources
LayoutInflater inflater = LayoutInflater.from(mContext);
mDummyStackView = new TaskStackView(mContext); mDummyStackView = new TaskStackView(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
reloadResources(); reloadResources();
} }
@@ -204,14 +201,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
Resources res = mContext.getResources(); Resources res = mContext.getResources();
reloadResources(); reloadResources();
mDummyStackView.reloadOnConfigurationChange(); mDummyStackView.reloadOnConfigurationChange();
// Update the header bar direction directly as it is not attached to anything and does not
// layout except in updateHeaderBarLayout()
mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
mHeaderBar.onConfigurationChanged();
mHeaderBar.forceLayout();
mHeaderBar.measure(
MeasureSpec.makeMeasureSpec(mHeaderBar.getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mHeaderBar.getMeasuredHeight(), MeasureSpec.EXACTLY));
} }
/** /**
@@ -582,7 +571,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
R.dimen.recents_task_view_header_height, R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land, R.dimen.recents_task_view_header_height_tablet_land,
R.dimen.recents_task_view_header_height, R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land); R.dimen.recents_task_view_header_height_tablet_land,
R.dimen.recents_grid_task_view_header_height);
LayoutInflater inflater = LayoutInflater.from(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
} }
/** /**
@@ -721,7 +716,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
if (task.isFreeformTask()) { if (task.isFreeformTask()) {
mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task, mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
stackScroller.getStackScroll(), mTmpTransform, null, stackScroller.getStackScroll(), mTmpTransform, null,
windowOverrideRect); windowOverrideRect, false /* useGridLayout */);
Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform, Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
mThumbTransitionBitmapCache); mThumbTransitionBitmapCache);
Rect toTaskRect = new Rect(); Rect toTaskRect = new Rect();
@@ -771,7 +766,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
stackView.updateLayoutAlgorithm(true /* boundScroll */); stackView.updateLayoutAlgorithm(true /* boundScroll */);
stackView.updateToInitialState(); stackView.updateToInitialState();
stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask, stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect); stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect,
Recents.getConfiguration().isGridEnabled);
return mTmpTransform; return mTmpTransform;
} }

View File

@@ -79,14 +79,15 @@ public class Utilities {
public static final Rect EMPTY_RECT = new Rect(); public static final Rect EMPTY_RECT = new Rect();
/** /**
* @return the first parent walking up the view hierarchy that has the given class type. * @return the first parent walking up the view hierarchy that has the given class type (or
* a subclass).
* *
* @param parentClass must be a class derived from {@link View} * @param parentClass must be a class derived from {@link View}
*/ */
public static <T extends View> T findParent(View v, Class<T> parentClass) { public static <T extends View> T findParent(View v, Class<T> parentClass) {
ViewParent parent = v.getParent(); ViewParent parent = v.getParent();
while (parent != null) { while (parent != null) {
if (parent.getClass().equals(parentClass)) { if (parentClass.isAssignableFrom(parent.getClass())) {
return (T) parent; return (T) parent;
} }
parent = parent.getParent(); parent = parent.getParent();

View File

@@ -849,6 +849,24 @@ public class TaskStack {
return null; return null;
} }
/**
* Returns the task in stack tasks which should be launched next if Recents are toggled
* again, or null if there is no task to be launched.
*/
public Task getNextLaunchTarget() {
int taskCount = getTaskCount();
if (taskCount == 0) {
return null;
}
int launchTaskIndex = indexOfStackTask(getLaunchTarget());
if (launchTaskIndex != -1) {
launchTaskIndex = Math.max(0, launchTaskIndex - 1);
} else {
launchTaskIndex = getTaskCount() - 1;
}
return getStackTasks().get(launchTaskIndex);
}
/** Returns the index of this task in this current task stack */ /** Returns the index of this task in this current task stack */
public int indexOfStackTask(Task t) { public int indexOfStackTask(Task t) {
return mStackTaskList.indexOf(t); return mStackTaskList.indexOf(t);

View File

@@ -30,17 +30,17 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
private static final float MIN_ALPHA = 0.1f; private static final float MIN_ALPHA = 0.1f;
private static final float MAX_ALPHA = 0.8f; private static final float MAX_ALPHA = 0.8f;
View mSourceView; protected View mSourceView;
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect(); protected Rect mClipRect = new Rect();
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
Rect mClipBounds = new Rect(); protected Rect mClipBounds = new Rect();
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
Rect mLastClipBounds = new Rect(); protected Rect mLastClipBounds = new Rect();
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
int mCornerRadius; protected int mCornerRadius;
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
float mAlpha = 1f; protected float mAlpha = 1f;
public AnimateableViewBounds(View source, int cornerRadius) { public AnimateableViewBounds(View source, int cornerRadius) {
mSourceView = source; mSourceView = source;
@@ -110,7 +110,7 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
return mClipRect.bottom; return mClipRect.bottom;
} }
private void updateClipBounds() { protected void updateClipBounds() {
mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top), mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
mSourceView.getWidth() - Math.max(0, mClipRect.right), mSourceView.getWidth() - Math.max(0, mClipRect.right),
mSourceView.getHeight() - Math.max(0, mClipRect.bottom)); mSourceView.getHeight() - Math.max(0, mClipRect.bottom));

View File

@@ -95,7 +95,6 @@ public class RecentsView extends FrameLayout {
private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134; private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100; private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
private TaskStack mStack;
private TaskStackView mTaskStackView; private TaskStackView mTaskStackView;
private TextView mStackActionButton; private TextView mStackActionButton;
private TextView mEmptyView; private TextView mEmptyView;
@@ -195,7 +194,6 @@ public class RecentsView extends FrameLayout {
* Called from RecentsActivity when the task stack is updated. * Called from RecentsActivity when the task stack is updated.
*/ */
public void updateStack(TaskStack stack, boolean setStackViewTasks) { public void updateStack(TaskStack stack, boolean setStackViewTasks) {
mStack = stack;
if (setStackViewTasks) { if (setStackViewTasks) {
mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */); mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
} }
@@ -212,7 +210,7 @@ public class RecentsView extends FrameLayout {
* Returns the current TaskStack. * Returns the current TaskStack.
*/ */
public TaskStack getStack() { public TaskStack getStack() {
return mStack; return mTaskStackView.getStack();
} }
/* /*
@@ -251,8 +249,7 @@ public class RecentsView extends FrameLayout {
/** Launches the task that recents was launched from if possible */ /** Launches the task that recents was launched from if possible */
public boolean launchPreviousTask() { public boolean launchPreviousTask() {
if (mTaskStackView != null) { if (mTaskStackView != null) {
TaskStack stack = mTaskStackView.getStack(); Task task = getStack().getLaunchTarget();
Task task = stack.getLaunchTarget();
if (task != null) { if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task); TaskView taskView = mTaskStackView.getChildViewForTask(task);
EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
@@ -340,7 +337,8 @@ public class RecentsView extends FrameLayout {
if (RecentsDebugFlags.Static.EnableStackActionButton) { if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Measure the stack action button within the constraints of the space above the stack // Measure the stack action button within the constraints of the space above the stack
Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect; Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect(
mTaskStackView.useGridLayout());
measureChild(mStackActionButton, measureChild(mStackActionButton,
MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST)); MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
@@ -437,8 +435,9 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(LaunchTaskEvent event) { public final void onBusEvent(LaunchTaskEvent event) {
mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView, mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack); event.taskView, event.screenPinningRequested, event.targetTaskBounds,
event.targetTaskStack);
} }
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
@@ -514,8 +513,7 @@ public class RecentsView extends FrameLayout {
EventBus.getDefault().send(new DockedFirstAnimationFrameEvent()); EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
// Remove the task and don't bother relaying out, as all the tasks will be // Remove the task and don't bother relaying out, as all the tasks will be
// relaid out when the stack changes on the multiwindow change event // relaid out when the stack changes on the multiwindow change event
mTaskStackView.getStack().removeTask(event.task, null, getStack().removeTask(event.task, null, true /* fromDockGesture */);
true /* fromDockGesture */);
} }
}; };
@@ -536,7 +534,7 @@ public class RecentsView extends FrameLayout {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
event.task.getTopComponent().flattenToShortString()); event.task.getTopComponent().flattenToShortString());
} else { } else {
EventBus.getDefault().send(new DragEndCancelledEvent(mStack, event.task, EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
event.taskView)); event.taskView));
} }
} else { } else {
@@ -598,7 +596,7 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) { public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
&& mStack.getTaskCount() > 0) { && getStack().getTaskCount() > 0) {
animateBackgroundScrim(1f, animateBackgroundScrim(1f,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION); TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
} }
@@ -775,7 +773,8 @@ public class RecentsView extends FrameLayout {
* @return the bounds of the stack action button. * @return the bounds of the stack action button.
*/ */
private Rect getStackActionButtonBoundsFromStackLayout() { private Rect getStackActionButtonBoundsFromStackLayout() {
Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect); Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect(
mTaskStackView.useGridLayout()));
int left = isLayoutRtl() int left = isLayoutRtl()
? actionButtonRect.left - mStackActionButton.getPaddingLeft() ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
: actionButtonRect.right + mStackActionButton.getPaddingRight() : actionButtonRect.right + mStackActionButton.getPaddingRight()
@@ -797,8 +796,8 @@ public class RecentsView extends FrameLayout {
writer.print(" [0x"); writer.print(id); writer.print("]"); writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println(); writer.println();
if (mStack != null) { if (getStack() != null) {
mStack.dump(innerPrefix, writer); getStack().dump(innerPrefix, writer);
} }
if (mTaskStackView != null) { if (mTaskStackView != null) {
mTaskStackView.dump(innerPrefix, writer); mTaskStackView.dump(innerPrefix, writer);

View File

@@ -157,7 +157,7 @@ public class TaskStackAnimationHelper {
// Get the current transform for the task, which will be used to position it offscreen // Get the current transform for the task, which will be used to position it offscreen
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform, stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null); null, mStackView.useGridLayout());
if (hideTask) { if (hideTask) {
tv.setVisibility(View.INVISIBLE); 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 // Get the current transform for the task, which will be updated to the final transform
// to animate to depending on how recents was invoked // to animate to depending on how recents was invoked
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform, stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null); null, mStackView.useGridLayout());
if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) { if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) { if (task.isLaunchTarget) {

View File

@@ -38,6 +38,7 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task; 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.TaskGridLayoutAlgorithm;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -240,14 +241,14 @@ public class TaskStackLayoutAlgorithm {
// This is the current system insets // This is the current system insets
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
public Rect mSystemInsets = new Rect(); public Rect mSystemInsets = new Rect();
// This is the bounds of the stack action above the stack rect
@ViewDebug.ExportedProperty(category="recents")
public Rect mStackActionButtonRect = new Rect();
// The visible ranges when the stack is focused and unfocused // The visible ranges when the stack is focused and unfocused
private Range mUnfocusedRange; private Range mUnfocusedRange;
private Range mFocusedRange; private Range mFocusedRange;
// This is the bounds of the stack action above the stack rect
@ViewDebug.ExportedProperty(category="recents")
private Rect mStackActionButtonRect = new Rect();
// The base top margin for the stack from the system insets // The base top margin for the stack from the system insets
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
private int mBaseTopMargin; private int mBaseTopMargin;
@@ -326,7 +327,7 @@ public class TaskStackLayoutAlgorithm {
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
int mMinTranslationZ; int mMinTranslationZ;
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
int mMaxTranslationZ; public int mMaxTranslationZ;
// Optimization, allows for quick lookup of task -> index // Optimization, allows for quick lookup of task -> index
private SparseIntArray mTaskIndexMap = new SparseIntArray(); private SparseIntArray mTaskIndexMap = new SparseIntArray();
@@ -334,6 +335,7 @@ public class TaskStackLayoutAlgorithm {
// The freeform workspace layout // The freeform workspace layout
FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm; FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
// The transform to place TaskViews at the front and back of the stack respectively // The transform to place TaskViews at the front and back of the stack respectively
TaskViewTransform mBackOfStackTransform = new TaskViewTransform(); TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
@@ -344,19 +346,7 @@ public class TaskStackLayoutAlgorithm {
mContext = context; mContext = context;
mCb = cb; mCb = cb;
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context); mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin); mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
mBaseTopMargin = getDimensionForDevice(context,
R.dimen.recents_layout_top_margin_phone,
R.dimen.recents_layout_top_margin_tablet,
R.dimen.recents_layout_top_margin_tablet_xlarge);
mBaseSideMargin = getDimensionForDevice(context,
R.dimen.recents_layout_side_margin_phone,
R.dimen.recents_layout_side_margin_tablet,
R.dimen.recents_layout_side_margin_tablet_xlarge);
mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
mFreeformStackGap =
res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
reloadOnConfigurationChange(context); reloadOnConfigurationChange(context);
} }
@@ -381,6 +371,7 @@ public class TaskStackLayoutAlgorithm {
R.dimen.recents_layout_initial_top_offset_tablet, R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet, R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet, R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet); R.dimen.recents_layout_initial_top_offset_tablet);
mBaseInitialBottomOffset = getDimensionForDevice(context, mBaseInitialBottomOffset = getDimensionForDevice(context,
R.dimen.recents_layout_initial_bottom_offset_phone_port, R.dimen.recents_layout_initial_bottom_offset_phone_port,
@@ -388,8 +379,24 @@ public class TaskStackLayoutAlgorithm {
R.dimen.recents_layout_initial_bottom_offset_tablet, R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet, R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet, R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet); R.dimen.recents_layout_initial_bottom_offset_tablet);
mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context); 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,
R.dimen.recents_layout_top_margin_tablet,
R.dimen.recents_layout_top_margin_tablet_xlarge,
R.dimen.recents_layout_top_margin_tablet);
mBaseSideMargin = getDimensionForDevice(context,
R.dimen.recents_layout_side_margin_phone,
R.dimen.recents_layout_side_margin_tablet,
R.dimen.recents_layout_side_margin_tablet_xlarge,
R.dimen.recents_layout_side_margin_tablet);
mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
mFreeformStackGap =
res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
} }
/** /**
@@ -406,6 +413,7 @@ public class TaskStackLayoutAlgorithm {
public boolean setSystemInsets(Rect systemInsets) { public boolean setSystemInsets(Rect systemInsets) {
boolean changed = !mSystemInsets.equals(systemInsets); boolean changed = !mSystemInsets.equals(systemInsets);
mSystemInsets.set(systemInsets); mSystemInsets.set(systemInsets);
mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
return changed; return changed;
} }
@@ -471,6 +479,9 @@ public class TaskStackLayoutAlgorithm {
updateFrontBackTransforms(); updateFrontBackTransforms();
} }
// Initialize the grid layout
mTaskGridLayoutAlgorithm.initialize(displayRect, windowRect);
} }
/** /**
@@ -722,6 +733,11 @@ public class TaskStackLayoutAlgorithm {
} }
} }
public Rect getStackActionButtonRect(boolean useGridLayout) {
return useGridLayout
? mTaskGridLayoutAlgorithm.getStackActionButtonRect() : mStackActionButtonRect;
}
/** /**
* Returns the TaskViewTransform that would put the task just off the back of the stack. * Returns the TaskViewTransform that would put the task just off the back of the stack.
*/ */
@@ -826,24 +842,30 @@ public class TaskStackLayoutAlgorithm {
* is what the view is measured and laid out with. * is what the view is measured and laid out with.
*/ */
public TaskViewTransform getStackTransform(Task task, float stackScroll, public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) { TaskViewTransform transformOut, TaskViewTransform frontTransform,
boolean useGridLayout) {
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
false /* forceUpdate */, false /* ignoreTaskOverrides */); false /* forceUpdate */, false /* ignoreTaskOverrides */, useGridLayout);
} }
public TaskViewTransform getStackTransform(Task task, float stackScroll, public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform, TaskViewTransform transformOut, TaskViewTransform frontTransform,
boolean ignoreTaskOverrides) { boolean ignoreTaskOverrides, boolean useGridLayout) {
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
false /* forceUpdate */, ignoreTaskOverrides); false /* forceUpdate */, ignoreTaskOverrides, useGridLayout);
} }
public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState, public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate, TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
boolean ignoreTaskOverrides) { boolean ignoreTaskOverrides, boolean useGridLayout) {
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) { if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this); mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
return transformOut; 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 { } else {
// Return early if we have an invalid index // Return early if we have an invalid index
int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1); int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
@@ -865,10 +887,10 @@ public class TaskStackLayoutAlgorithm {
*/ */
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll, public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform, TaskViewTransform transformOut, TaskViewTransform frontTransform,
Rect windowOverrideRect) { Rect windowOverrideRect, boolean useGridLayout) {
TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState, TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
transformOut, frontTransform, true /* forceUpdate */, transformOut, frontTransform, true /* forceUpdate */,
false /* ignoreTaskOverrides */); false /* ignoreTaskOverrides */, useGridLayout);
return transformToScreenCoordinates(transform, windowOverrideRect); return transformToScreenCoordinates(transform, windowOverrideRect);
} }
@@ -1089,9 +1111,9 @@ public class TaskStackLayoutAlgorithm {
* Retrieves resources that are constant regardless of the current configuration of the device. * Retrieves resources that are constant regardless of the current configuration of the device.
*/ */
public static int getDimensionForDevice(Context ctx, int phoneResId, public static int getDimensionForDevice(Context ctx, int phoneResId,
int tabletResId, int xlargeTabletResId) { int tabletResId, int xlargeTabletResId, int gridLayoutResId) {
return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId, return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
xlargeTabletResId, xlargeTabletResId); xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
} }
/** /**
@@ -1099,12 +1121,14 @@ public class TaskStackLayoutAlgorithm {
*/ */
public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId, public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId, int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
int xlargeTabletLandResId) { int xlargeTabletLandResId, int gridLayoutResId) {
RecentsConfiguration config = Recents.getConfiguration(); RecentsConfiguration config = Recents.getConfiguration();
Resources res = ctx.getResources(); Resources res = ctx.getResources();
boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation == boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
Configuration.ORIENTATION_LANDSCAPE; Configuration.ORIENTATION_LANDSCAPE;
if (config.isXLargeScreen) { if (config.isGridEnabled) {
return res.getDimensionPixelSize(gridLayoutResId);
} else if (config.isXLargeScreen) {
return res.getDimensionPixelSize(isLandscape return res.getDimensionPixelSize(isLandscape
? xlargeTabletLandResId ? xlargeTabletLandResId
: xlargeTabletPortResId); : xlargeTabletPortResId);

View File

@@ -92,6 +92,7 @@ import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task; 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 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;
@@ -158,6 +159,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
private int mTaskCornerRadiusPx; private int mTaskCornerRadiusPx;
private int mDividerSize; private int mDividerSize;
private int mStartTimerIndicatorDuration; private int mStartTimerIndicatorDuration;
private boolean mDraggingOverDockState;
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
private boolean mTaskViewsClipDirty = true; private boolean mTaskViewsClipDirty = true;
@@ -279,6 +281,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
}); });
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
if (ssp.hasFreeformWorkspaceSupport()) {
setWillNotDraw(false);
}
mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable( mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
R.drawable.recents_freeform_workspace_bg); R.drawable.recents_freeform_workspace_bg);
@@ -496,13 +501,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Calculate the current and (if necessary) the target transform for the task // Calculate the current and (if necessary) the target transform for the task
transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll, transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
taskTransforms.get(i), frontTransform, ignoreTaskOverrides); taskTransforms.get(i), frontTransform, ignoreTaskOverrides, useGridLayout());
if (useTargetStackScroll && !transform.visible) { if (useTargetStackScroll && !transform.visible) {
// If we have a target stack scroll and the task is not currently visible, then we // If we have a target stack scroll and the task is not currently visible, then we
// just update the transform at the new scroll // just update the transform at the new scroll
// TODO: Optimize this // TODO: Optimize this
transformAtTarget = mLayoutAlgorithm.getStackTransform(task, transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
targetStackScroll, new TaskViewTransform(), frontTransformAtTarget); new TaskViewTransform(), frontTransformAtTarget, useGridLayout());
if (transformAtTarget.visible) { if (transformAtTarget.visible) {
transform.copyFrom(transformAtTarget); transform.copyFrom(transformAtTarget);
} }
@@ -733,7 +738,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} else { } else {
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
focusState, transform, null, true /* forceUpdate */, focusState, transform, null, true /* forceUpdate */,
false /* ignoreTaskOverrides */); false /* ignoreTaskOverrides */, useGridLayout());
} }
transform.visible = true; transform.visible = true;
} }
@@ -750,7 +755,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Task task = tasks.get(i); Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i); TaskViewTransform transform = transformsOut.get(i);
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null, mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
true /* forceUpdate */, ignoreTaskOverrides); true /* forceUpdate */, ignoreTaskOverrides, useGridLayout());
transform.visible = true; transform.visible = true;
} }
} }
@@ -779,6 +784,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
* Updates the clip for each of the task views from back to front. * Updates the clip for each of the task views from back to front.
*/ */
private void clipTaskViews() { private void clipTaskViews() {
// We never clip task views in grid layout
if (Recents.getConfiguration().isGridEnabled) {
return;
}
// Update the clip on each task child // Update the clip on each task child
List<TaskView> taskViews = getTaskViews(); List<TaskView> taskViews = getTaskViews();
TaskView tmpTv = null; TaskView tmpTv = null;
@@ -1335,14 +1345,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
setFocusedTask(focusedTaskIndex, false /* scrollToTask */, setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
false /* requestViewFocus */); false /* requestViewFocus */);
} }
updateStackActionButtonVisibility();
// Update the stack action button visibility
if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) {
EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
} else {
EventBus.getDefault().send(new HideStackActionButtonEvent());
}
} }
public boolean isTouchPointInView(float x, float y, TaskView tv) { public boolean isTouchPointInView(float x, float y, TaskView tv) {
@@ -1503,7 +1506,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override @Override
public TaskView createView(Context context) { public TaskView createView(Context context) {
return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false); if (Recents.getConfiguration().isGridEnabled) {
return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
} else {
return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
}
} }
@Override @Override
@@ -1560,11 +1567,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Bind the task view to the new task // Bind the task view to the new task
bindTaskView(tv, task); bindTaskView(tv, task);
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.isAsleep()) {
tv.setNoUserInteractionState();
}
// Set the new state for this view, including the callbacks and view clipping // Set the new state for this view, including the callbacks and view clipping
tv.setCallbacks(this); tv.setCallbacks(this);
tv.setTouchEnabled(true); tv.setTouchEnabled(true);
@@ -1594,6 +1596,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Rebind the task and request that this task's data be filled into the TaskView // Rebind the task and request that this task's data be filled into the TaskView
tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect); tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.isAsleep() ||
Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
tv.setNoUserInteractionState();
}
// Load the task data // Load the task data
Recents.getTaskLoader().loadTaskData(task); Recents.getTaskLoader().loadTaskData(task);
} }
@@ -1632,7 +1640,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
relayoutTaskViewsOnNextFrame(animation); relayoutTaskViewsOnNextFrame(animation);
} }
if (mEnterAnimationComplete) { // In grid layout, the stack action button always remains visible.
if (mEnterAnimationComplete && !useGridLayout()) {
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD && if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD && curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) { mStack.getTaskCount() > 0) {
@@ -1679,17 +1688,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return; return;
} }
int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget()); final Task launchTask = mStack.getNextLaunchTarget();
if (launchTaskIndex != -1) { if (launchTask != null) {
launchTaskIndex = Math.max(0, launchTaskIndex - 1);
} else {
launchTaskIndex = mStack.getTaskCount() - 1;
}
if (launchTaskIndex != -1) {
// Stop all animations // Stop all animations
cancelAllTaskViewAnimations(); cancelAllTaskViewAnimations();
final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
float curScroll = mStackScroller.getStackScroll(); float curScroll = mStackScroller.getStackScroll();
float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask); float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
float absScrollDiff = Math.abs(targetScroll - curScroll); float absScrollDiff = Math.abs(targetScroll - curScroll);
@@ -1832,7 +1835,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Enlarge the dragged view slightly // Enlarge the dragged view slightly
float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR; float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(), mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
mTmpTransform, null); mTmpTransform, null, useGridLayout());
mTmpTransform.scale = finalScale; mTmpTransform.scale = finalScale;
mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1; mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
mTmpTransform.dimAlpha = 0f; mTmpTransform.dimAlpha = 0f;
@@ -1853,6 +1856,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Interpolators.FAST_OUT_SLOW_IN); Interpolators.FAST_OUT_SLOW_IN);
boolean ignoreTaskOverrides = false; boolean ignoreTaskOverrides = false;
if (event.dropTarget instanceof TaskStack.DockState) { if (event.dropTarget instanceof TaskStack.DockState) {
mDraggingOverDockState = true;
// Calculate the new task stack bounds that matches the window size that Recents will // Calculate the new task stack bounds that matches the window size that Recents will
// have after the drop // have after the drop
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
@@ -1872,6 +1876,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
updateLayoutAlgorithm(true /* boundScroll */); updateLayoutAlgorithm(true /* boundScroll */);
ignoreTaskOverrides = true; ignoreTaskOverrides = true;
} else { } else {
mDraggingOverDockState = false;
// Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging // 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 // task view, so add it back to the ignore set after updating the layout
removeIgnoreTask(event.task); removeIgnoreTask(event.task);
@@ -1882,6 +1887,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
public final void onBusEvent(final DragEndEvent event) { public final void onBusEvent(final DragEndEvent event) {
mDraggingOverDockState = false;
// We don't handle drops on the dock regions // We don't handle drops on the dock regions
if (event.dropTarget instanceof TaskStack.DockState) { if (event.dropTarget instanceof TaskStack.DockState) {
// However, we do need to reset the overrides, since the last state of this task stack // However, we do need to reset the overrides, since the last state of this task stack
@@ -1969,7 +1975,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override @Override
public void run() { public void run() {
// Start the dozer to trigger to trigger any UI that shows after a timeout // Start the dozer to trigger to trigger any UI that shows after a timeout
mUIDozeTrigger.startDozing(); if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
mUIDozeTrigger.startDozing();
}
// Update the focused state here -- since we only set the focused task without // Update the focused state here -- since we only set the focused task without
// requesting view focus in onFirstLayout(), actually request view focus and // requesting view focus in onFirstLayout(), actually request view focus and
@@ -2049,6 +2057,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
} }
// Update the Clear All button in case we're switching in or out of grid layout.
updateStackActionButtonVisibility();
// Trigger a new layout and update to the initial state if necessary // Trigger a new layout and update to the initial state if necessary
if (event.fromMultiWindow) { if (event.fromMultiWindow) {
mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY; mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
@@ -2118,6 +2129,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return -1; 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. * Reads current system flags related to accessibility and screen pinning.
*/ */
@@ -2128,6 +2153,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Settings.System.LOCK_TO_APP_ENABLED) != 0; Settings.System.LOCK_TO_APP_ENABLED) != 0;
} }
private void updateStackActionButtonVisibility() {
// Always show the button in grid layout.
if (useGridLayout() ||
(mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0)) {
EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
} else {
EventBus.getDefault().send(new HideStackActionButtonEvent());
}
}
public void dump(String prefix, PrintWriter writer) { public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " "; String innerPrefix = prefix + " ";
String id = Integer.toHexString(System.identityHashCode(this)); String id = Integer.toHexString(System.identityHashCode(this));

View File

@@ -148,7 +148,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
private ArrayList<Animator> mTmpAnimators = new ArrayList<>(); private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
@ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_") @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
TaskViewThumbnail mThumbnailView; protected TaskViewThumbnail mThumbnailView;
@ViewDebug.ExportedProperty(deepExport=true, prefix="header_") @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView; TaskViewHeader mHeaderView;
private View mActionButtonView; private View mActionButtonView;
@@ -176,8 +176,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
RecentsConfiguration config = Recents.getConfiguration(); RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources(); Resources res = context.getResources();
mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize( mViewBounds = createOutlineProvider();
R.dimen.recents_task_view_shadow_rounded_corners_radius));
if (config.fakeShadows) { if (config.fakeShadows) {
setBackground(new FakeShadowDrawable(res, config)); setBackground(new FakeShadowDrawable(res, config));
} }
@@ -194,7 +193,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
* Called from RecentsActivity when it is relaunched. * Called from RecentsActivity when it is relaunched.
*/ */
void onReload(boolean isResumingFromVisible) { void onReload(boolean isResumingFromVisible) {
resetNoUserInteractionState(); if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
resetNoUserInteractionState();
}
if (!isResumingFromVisible) { if (!isResumingFromVisible) {
resetViewProperties(); resetViewProperties();
} }
@@ -205,6 +206,12 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
return mTask; return mTask;
} }
/* Create an outline provider to clip and outline the view */
protected AnimateableViewBounds createOutlineProvider() {
return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_view_shadow_rounded_corners_radius));
}
/** Returns the view bounds. */ /** Returns the view bounds. */
AnimateableViewBounds getViewBounds() { AnimateableViewBounds getViewBounds() {
return mViewBounds; return mViewBounds;
@@ -232,7 +239,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
/** /**
* Update the task view when the configuration changes. * Update the task view when the configuration changes.
*/ */
void onConfigurationChanged() { protected void onConfigurationChanged() {
mHeaderView.onConfigurationChanged(); mHeaderView.onConfigurationChanged();
} }
@@ -666,10 +673,16 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
SystemServicesProxy ssp = Recents.getSystemServices(); SystemServicesProxy ssp = Recents.getSystemServices();
// Since we are clipping the view to the bounds, manually do the hit test boolean inBounds = false;
Rect clipBounds = new Rect(mViewBounds.mClipBounds); Rect clipBounds = new Rect(mViewBounds.mClipBounds);
clipBounds.scale(getScaleX()); if (!clipBounds.isEmpty()) {
boolean inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y); // If we are clipping the view to the bounds, manually do the hit test.
clipBounds.scale(getScaleX());
inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
} else {
// Otherwise just make sure we're within the view's bounds.
inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
}
if (v == this && inBounds && !ssp.hasDockedTask()) { if (v == this && inBounds && !ssp.hasDockedTask()) {
// Start listening for drag events // Start listening for drag events
setClipViewInStack(false); setClipViewInStack(false);

View File

@@ -290,14 +290,16 @@ public class TaskViewHeader extends FrameLayout
R.dimen.recents_task_view_header_height, R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land, R.dimen.recents_task_view_header_height_tablet_land,
R.dimen.recents_task_view_header_height, R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land); R.dimen.recents_task_view_header_height_tablet_land,
R.dimen.recents_grid_task_view_header_height);
int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(), int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
R.dimen.recents_task_view_header_button_padding, R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding, R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding, R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding_tablet_land, R.dimen.recents_task_view_header_button_padding_tablet_land,
R.dimen.recents_task_view_header_button_padding, R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding_tablet_land); R.dimen.recents_task_view_header_button_padding_tablet_land,
R.dimen.recents_grid_task_view_header_button_padding);
if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) { if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
mHeaderBarHeight = headerBarHeight; mHeaderBarHeight = headerBarHeight;
mHeaderButtonPadding = headerButtonPadding; mHeaderButtonPadding = headerButtonPadding;
@@ -603,10 +605,7 @@ public class TaskViewHeader extends FrameLayout
Constants.Metrics.DismissSourceHeaderButton); Constants.Metrics.DismissSourceHeaderButton);
} else if (v == mMoveTaskButton) { } else if (v == mMoveTaskButton) {
TaskView tv = Utilities.findParent(this, TaskView.class); TaskView tv = Utilities.findParent(this, TaskView.class);
Rect bounds = mMoveTaskTargetStackId == FREEFORM_WORKSPACE_STACK_ID EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null,
? new Rect(mTaskViewRect)
: new Rect();
EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, bounds,
mMoveTaskTargetStackId, false)); mMoveTaskTargetStackId, false));
} else if (v == mAppInfoView) { } else if (v == mAppInfoView) {
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask)); EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));

View File

@@ -60,6 +60,8 @@ public class TaskViewThumbnail extends View {
@ViewDebug.ExportedProperty(category="recents") @ViewDebug.ExportedProperty(category="recents")
private float mThumbnailScale; private float mThumbnailScale;
private float mFullscreenThumbnailScale; private float mFullscreenThumbnailScale;
private boolean mSizeToFit = false;
private boolean mOverlayHeaderOnThumbnailActionBar = true;
private ActivityManager.TaskThumbnailInfo mThumbnailInfo; private ActivityManager.TaskThumbnailInfo mThumbnailInfo;
private int mCornerRadius; private int mCornerRadius;
@@ -133,10 +135,12 @@ public class TaskViewThumbnail extends View {
(int) (mThumbnailRect.width() * mThumbnailScale)); (int) (mThumbnailRect.width() * mThumbnailScale));
int thumbnailHeight = Math.min(viewHeight, int thumbnailHeight = Math.min(viewHeight,
(int) (mThumbnailRect.height() * mThumbnailScale)); (int) (mThumbnailRect.height() * mThumbnailScale));
if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
int topOffset = mTaskBar != null int topOffset = 0;
? mTaskBar.getHeight() - mCornerRadius if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
: 0; topOffset = mTaskBar.getHeight() - mCornerRadius;
}
// Draw the background, there will be some small overdraw with the thumbnail // Draw the background, there will be some small overdraw with the thumbnail
if (thumbnailWidth < viewWidth) { if (thumbnailWidth < viewWidth) {
@@ -230,7 +234,7 @@ public class TaskViewThumbnail extends View {
// If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
// and only draw the background color // and only draw the background color
mThumbnailScale = 0f; mThumbnailScale = 0f;
} else if (isStackTask) { } else if (isStackTask && !mSizeToFit) {
float invThumbnailScale = 1f / mFullscreenThumbnailScale; float invThumbnailScale = 1f / mFullscreenThumbnailScale;
if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) { if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) { if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -262,6 +266,19 @@ public class TaskViewThumbnail extends View {
} }
} }
/** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
public void setSizeToFit(boolean flag) {
mSizeToFit = flag;
}
/**
* Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
* be stacked just above it.
*/
public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
mOverlayHeaderOnThumbnailActionBar = flag;
}
/** Updates the clip rect based on the given task bar. */ /** Updates the clip rect based on the given task bar. */
void updateClipToTaskBar(View taskBar) { void updateClipToTaskBar(View taskBar) {
mTaskBar = taskBar; mTaskBar = taskBar;

View File

@@ -0,0 +1,32 @@
/*
* 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.view.View;
import com.android.systemui.recents.views.AnimateableViewBounds;
/* An outline provider for grid-based task views. */
class AnimateableGridViewBounds extends AnimateableViewBounds {
public AnimateableGridViewBounds(View source, int cornerRadius) {
super(source, cornerRadius);
}
@Override
protected void updateClipBounds() {
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.util.AttributeSet;
import com.android.systemui.R;
import com.android.systemui.recents.views.AnimateableViewBounds;
import com.android.systemui.recents.views.TaskView;
public class GridTaskView extends TaskView {
/** The height, in pixels, of the header view. */
private int mHeaderHeight;
public GridTaskView(Context context) {
this(context, null);
}
public GridTaskView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mHeaderHeight = context.getResources().getDimensionPixelSize(
R.dimen.recents_grid_task_view_header_height);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Show the full thumbnail and don't overlap with the header.
mThumbnailView.setSizeToFit(true);
mThumbnailView.setOverlayHeaderOnThumbnailActionBar(false);
mThumbnailView.updateThumbnailScale();
mThumbnailView.setTranslationY(mHeaderHeight);
}
@Override
protected AnimateableViewBounds createOutlineProvider() {
return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_view_shadow_rounded_corners_radius));
}
@Override
protected void onConfigurationChanged() {
super.onConfigurationChanged();
mHeaderHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_grid_task_view_header_height);
mThumbnailView.setTranslationY(mHeaderHeight);
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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.Point;
import android.graphics.Rect;
import android.view.WindowManager;
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 final String TAG = "TaskGridLayoutAlgorithm";
private final int MAX_LAYOUT_TASK_COUNT = 8;
/** The horizontal padding around the whole recents view. */
private int mPaddingLeftRight;
/** The vertical padding around the whole recents view. */
private int mPaddingTopBottom;
/** The padding between task views. */
private int mPaddingTaskView;
private Rect mDisplayRect;
private Rect mWindowRect;
private Point mScreenSize = new Point();
private Rect mTaskGridRect;
/** The height, in pixels, of each task view's title bar. */
private int mTitleBarHeight;
/** The aspect ratio of each task thumbnail, without the title bar. */
private float mAppAspectRatio;
private Rect mSystemInsets = new Rect();
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();
mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getRealSize(mScreenSize);
updateAppAspectRatio();
}
public TaskViewTransform getTransform(int taskIndex, int taskCount,
TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
// We also need to invert the index in order to display the most recent tasks first.
int taskLayoutIndex = taskCount - taskIndex - 1;
int tasksPerLine = layoutTaskCount < 2 ? 1 : (
layoutTaskCount < 5 ? 2 : (
layoutTaskCount < 7 ? 3 : 4));
int lines = layoutTaskCount < 3 ? 1 : 2;
int taskWidth, taskHeight;
int maxTaskWidth = (mDisplayRect.width() - 2 * mPaddingLeftRight
- (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
int maxTaskHeight = (mDisplayRect.height() - 2 * mPaddingTopBottom
- (lines - 1) * mPaddingTaskView) / lines;
if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
// Width bound.
taskWidth = maxTaskWidth;
taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight);
} else {
// Height bound.
taskHeight = maxTaskHeight;
taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio);
}
int emptySpaceX = mDisplayRect.width() - 2 * mPaddingLeftRight
- (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
int emptySpaceY = mDisplayRect.height() - 2 * mPaddingTopBottom
- (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
mTaskGridRect.set(0, 0, taskWidth, taskHeight);
int xIndex = taskLayoutIndex % tasksPerLine;
int yIndex = taskLayoutIndex / tasksPerLine;
int x = emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
int y = emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
float z = stackLayout.mMaxTranslationZ;
float dimAlpha = 0f;
float viewOutlineAlpha = 0f;
boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
// Fill out the transform
transformOut.scale = 1f;
transformOut.alpha = isTaskViewVisible ? 1f : 0f;
transformOut.translationZ = z;
transformOut.dimAlpha = dimAlpha;
transformOut.viewOutlineAlpha = viewOutlineAlpha;
transformOut.rect.set(mTaskGridRect);
transformOut.rect.offset(x, y);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
// We only show the 8 most recent tasks.
transformOut.visible = isTaskViewVisible;
return transformOut;
}
public void initialize(Rect displayRect, Rect windowRect) {
mDisplayRect = displayRect;
mWindowRect = windowRect;
}
public void setSystemInsets(Rect systemInsets) {
mSystemInsets = systemInsets;
updateAppAspectRatio();
}
private void updateAppAspectRatio() {
int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
mAppAspectRatio = (float) usableWidth / (float) usableHeight;
}
public Rect getStackActionButtonRect() {
Rect buttonRect = new Rect(mDisplayRect);
buttonRect.right -= mPaddingLeftRight;
buttonRect.left += mPaddingLeftRight;
buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
return buttonRect;
}
}