DO NOT MERGE - Port 2D recents implementation from master to nyc-mr2
Bug: 32101881
Test: Checked Recents layout/behavior on local sw600dp device
Change-Id: I40be7dbaf8bc017b4c7c449f9bca657817107ceb
diff --git a/packages/SystemUI/res/layout/recents_grid_task_view.xml b/packages/SystemUI/res/layout/recents_grid_task_view.xml
new file mode 100644
index 0000000..53bec70
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_grid_task_view.xml
@@ -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>
+
+
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index c8e5b61..015e4a2 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -4,9 +4,9 @@
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.
@@ -26,35 +26,10 @@
<include layout="@layout/recents_task_view_header" />
<!-- TODO: Move this into a view stub -->
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- 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>
+ <include layout="@layout/recents_task_view_lock_to_app"/>
<!-- The incompatible app toast -->
- <ViewStub 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" />
+ <include layout="@layout/recents_task_view_incompatible_app_toast"/>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml b/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml
new file mode 100644
index 0000000..d573d6b
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml
@@ -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" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml b/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml
new file mode 100644
index 0000000..8cece11
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml
@@ -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>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
new file mode 100644
index 0000000..94ffdb1
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -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>
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index eb9beb6..ad229ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -207,8 +207,6 @@
getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
mImpl = new RecentsTvImpl(mContext);
- } else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) {
- mImpl = new RecentsGridImpl(mContext);
} else {
mImpl = new RecentsImpl(mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 73c6e6e..711f0c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.SystemProperties;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -58,6 +59,10 @@
public boolean fakeShadows;
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) {
// Load only resources that can not change after the first load either through developer
// settings or via multi window
@@ -66,6 +71,7 @@
Resources res = appContext.getResources();
fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
svelteLevel = res.getInteger(R.integer.recents_svelte_level);
+ isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
float screenDensity = context.getResources().getDisplayMetrics().density;
smallestWidth = ssp.getDeviceSmallestWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 42d1b61..2263a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -180,10 +180,7 @@
ssp.registerTaskStackListener(mTaskStackListener);
// Initialize the static configuration resources
- LayoutInflater inflater = LayoutInflater.from(mContext);
mDummyStackView = new TaskStackView(mContext);
- mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
- null, false);
reloadResources();
}
@@ -204,14 +201,6 @@
Resources res = mContext.getResources();
reloadResources();
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 @@
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land,
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 @@
if (task.isFreeformTask()) {
mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
stackScroller.getStackScroll(), mTmpTransform, null,
- windowOverrideRect);
+ windowOverrideRect, false /* useGridLayout */);
Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
mThumbTransitionBitmapCache);
Rect toTaskRect = new Rect();
@@ -771,7 +766,8 @@
stackView.updateLayoutAlgorithm(true /* boundScroll */);
stackView.updateToInitialState();
stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
- stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
+ stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect,
+ Recents.getConfiguration().isGridEnabled);
return mTmpTransform;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 2c5c437..d64fc36 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -79,14 +79,15 @@
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}
*/
public static <T extends View> T findParent(View v, Class<T> parentClass) {
ViewParent parent = v.getParent();
while (parent != null) {
- if (parent.getClass().equals(parentClass)) {
+ if (parentClass.isAssignableFrom(parent.getClass())) {
return (T) parent;
}
parent = parent.getParent();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 745f5a5..178cb9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -849,6 +849,24 @@
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 */
public int indexOfStackTask(Task t) {
return mStackTaskList.indexOf(t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 253d06a..dba085e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -30,17 +30,17 @@
private static final float MIN_ALPHA = 0.1f;
private static final float MAX_ALPHA = 0.8f;
- View mSourceView;
+ protected View mSourceView;
@ViewDebug.ExportedProperty(category="recents")
- Rect mClipRect = new Rect();
+ protected Rect mClipRect = new Rect();
@ViewDebug.ExportedProperty(category="recents")
- Rect mClipBounds = new Rect();
+ protected Rect mClipBounds = new Rect();
@ViewDebug.ExportedProperty(category="recents")
- Rect mLastClipBounds = new Rect();
+ protected Rect mLastClipBounds = new Rect();
@ViewDebug.ExportedProperty(category="recents")
- int mCornerRadius;
+ protected int mCornerRadius;
@ViewDebug.ExportedProperty(category="recents")
- float mAlpha = 1f;
+ protected float mAlpha = 1f;
public AnimateableViewBounds(View source, int cornerRadius) {
mSourceView = source;
@@ -110,7 +110,7 @@
return mClipRect.bottom;
}
- private void updateClipBounds() {
+ protected void updateClipBounds() {
mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
mSourceView.getWidth() - Math.max(0, mClipRect.right),
mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 24ef433..8d32c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -95,7 +95,6 @@
private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
- private TaskStack mStack;
private TaskStackView mTaskStackView;
private TextView mStackActionButton;
private TextView mEmptyView;
@@ -195,7 +194,6 @@
* Called from RecentsActivity when the task stack is updated.
*/
public void updateStack(TaskStack stack, boolean setStackViewTasks) {
- mStack = stack;
if (setStackViewTasks) {
mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
}
@@ -212,7 +210,7 @@
* Returns the current TaskStack.
*/
public TaskStack getStack() {
- return mStack;
+ return mTaskStackView.getStack();
}
/*
@@ -251,8 +249,7 @@
/** Launches the task that recents was launched from if possible */
public boolean launchPreviousTask() {
if (mTaskStackView != null) {
- TaskStack stack = mTaskStackView.getStack();
- Task task = stack.getLaunchTarget();
+ Task task = getStack().getLaunchTarget();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
@@ -340,7 +337,8 @@
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// 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,
MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
@@ -437,8 +435,9 @@
public final void onBusEvent(LaunchTaskEvent event) {
mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
- mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView,
- event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack);
+ mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
+ event.taskView, event.screenPinningRequested, event.targetTaskBounds,
+ event.targetTaskStack);
}
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
@@ -514,8 +513,7 @@
EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
// 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
- mTaskStackView.getStack().removeTask(event.task, null,
- true /* fromDockGesture */);
+ getStack().removeTask(event.task, null, true /* fromDockGesture */);
}
};
@@ -536,7 +534,7 @@
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
event.task.getTopComponent().flattenToShortString());
} else {
- EventBus.getDefault().send(new DragEndCancelledEvent(mStack, event.task,
+ EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
event.taskView));
}
} else {
@@ -598,7 +596,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
- && mStack.getTaskCount() > 0) {
+ && getStack().getTaskCount() > 0) {
animateBackgroundScrim(1f,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
@@ -775,7 +773,8 @@
* @return the bounds of the stack action button.
*/
private Rect getStackActionButtonBoundsFromStackLayout() {
- Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect);
+ Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect(
+ mTaskStackView.useGridLayout()));
int left = isLayoutRtl()
? actionButtonRect.left - mStackActionButton.getPaddingLeft()
: actionButtonRect.right + mStackActionButton.getPaddingRight()
@@ -797,8 +796,8 @@
writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println();
- if (mStack != null) {
- mStack.dump(innerPrefix, writer);
+ if (getStack() != null) {
+ getStack().dump(innerPrefix, writer);
}
if (mTaskStackView != null) {
mTaskStackView.dump(innerPrefix, writer);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 493e618..c1f4c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -157,7 +157,7 @@
// 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 @@
// 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) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index fce7f9d..f0644a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -38,6 +38,7 @@
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;
@@ -240,14 +241,14 @@
// This is the current system insets
@ViewDebug.ExportedProperty(category="recents")
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
private Range mUnfocusedRange;
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
@ViewDebug.ExportedProperty(category="recents")
private int mBaseTopMargin;
@@ -326,7 +327,7 @@
@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 +335,7 @@
// 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,19 +346,7 @@
mContext = context;
mCb = cb;
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(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);
- 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);
-
+ mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
reloadOnConfigurationChange(context);
}
@@ -381,6 +371,7 @@
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,
R.dimen.recents_layout_initial_bottom_offset_phone_port,
@@ -388,8 +379,24 @@
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);
+ 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 boolean setSystemInsets(Rect systemInsets) {
boolean changed = !mSystemInsets.equals(systemInsets);
mSystemInsets.set(systemInsets);
+ mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
return changed;
}
@@ -471,6 +479,9 @@
updateFrontBackTransforms();
}
+
+ // Initialize the grid layout
+ mTaskGridLayoutAlgorithm.initialize(displayRect, windowRect);
}
/**
@@ -722,6 +733,11 @@
}
}
+ 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.
*/
@@ -826,24 +842,30 @@
* 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);
@@ -865,10 +887,10 @@
*/
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform,
- Rect windowOverrideRect) {
+ Rect windowOverrideRect, boolean useGridLayout) {
TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
transformOut, frontTransform, true /* forceUpdate */,
- false /* ignoreTaskOverrides */);
+ false /* ignoreTaskOverrides */, useGridLayout);
return transformToScreenCoordinates(transform, windowOverrideRect);
}
@@ -1089,9 +1111,9 @@
* Retrieves resources that are constant regardless of the current configuration of the device.
*/
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,
- xlargeTabletResId, xlargeTabletResId);
+ xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
}
/**
@@ -1099,12 +1121,14 @@
*/
public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
- int xlargeTabletLandResId) {
+ int xlargeTabletLandResId, int gridLayoutResId) {
RecentsConfiguration config = Recents.getConfiguration();
Resources res = ctx.getResources();
boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
Configuration.ORIENTATION_LANDSCAPE;
- if (config.isXLargeScreen) {
+ if (config.isGridEnabled) {
+ return res.getDimensionPixelSize(gridLayoutResId);
+ } else if (config.isXLargeScreen) {
return res.getDimensionPixelSize(isLandscape
? xlargeTabletLandResId
: xlargeTabletPortResId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fc580a5..8d91b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -92,6 +92,7 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.grid.GridTaskView;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -158,6 +159,7 @@
private int mTaskCornerRadiusPx;
private int mDividerSize;
private int mStartTimerIndicatorDuration;
+ private boolean mDraggingOverDockState;
@ViewDebug.ExportedProperty(category="recents")
private boolean mTaskViewsClipDirty = true;
@@ -279,6 +281,9 @@
}
});
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ setWillNotDraw(false);
+ }
mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
R.drawable.recents_freeform_workspace_bg);
@@ -496,13 +501,13 @@
// 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);
}
@@ -733,7 +738,7 @@
} else {
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
focusState, transform, null, true /* forceUpdate */,
- false /* ignoreTaskOverrides */);
+ false /* ignoreTaskOverrides */, useGridLayout());
}
transform.visible = true;
}
@@ -750,7 +755,7 @@
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;
}
}
@@ -779,6 +784,11 @@
* Updates the clip for each of the task views from back to front.
*/
private void clipTaskViews() {
+ // We never clip task views in grid layout
+ if (Recents.getConfiguration().isGridEnabled) {
+ return;
+ }
+
// Update the clip on each task child
List<TaskView> taskViews = getTaskViews();
TaskView tmpTv = null;
@@ -1335,14 +1345,7 @@
setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
false /* requestViewFocus */);
}
-
- // 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());
- }
+ updateStackActionButtonVisibility();
}
public boolean isTouchPointInView(float x, float y, TaskView tv) {
@@ -1503,7 +1506,11 @@
@Override
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
@@ -1560,11 +1567,6 @@
// Bind the task view to the new 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
tv.setCallbacks(this);
tv.setTouchEnabled(true);
@@ -1594,6 +1596,12 @@
// Rebind the task and request that this task's data be filled into the TaskView
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
Recents.getTaskLoader().loadTaskData(task);
}
@@ -1632,7 +1640,8 @@
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 &&
curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) {
@@ -1679,17 +1688,11 @@
return;
}
- int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
- if (launchTaskIndex != -1) {
- launchTaskIndex = Math.max(0, launchTaskIndex - 1);
- } else {
- launchTaskIndex = mStack.getTaskCount() - 1;
- }
- if (launchTaskIndex != -1) {
+ final Task launchTask = mStack.getNextLaunchTarget();
+ if (launchTask != null) {
// Stop all animations
cancelAllTaskViewAnimations();
- final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
float curScroll = mStackScroller.getStackScroll();
float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
float absScrollDiff = Math.abs(targetScroll - curScroll);
@@ -1832,7 +1835,7 @@
// 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;
@@ -1853,6 +1856,7 @@
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;
@@ -1872,6 +1876,7 @@
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);
@@ -1882,6 +1887,7 @@
}
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
@@ -1969,7 +1975,9 @@
@Override
public void run() {
// 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
// requesting view focus in onFirstLayout(), actually request view focus and
@@ -2049,6 +2057,9 @@
}
}
+ // 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
if (event.fromMultiWindow) {
mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
@@ -2119,6 +2130,20 @@
}
/**
+ * 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.
*/
private void readSystemFlags() {
@@ -2128,6 +2153,17 @@
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) {
String innerPrefix = prefix + " ";
String id = Integer.toHexString(System.identityHashCode(this));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 115e65a..93ab0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -148,7 +148,7 @@
private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
@ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
- TaskViewThumbnail mThumbnailView;
+ protected TaskViewThumbnail mThumbnailView;
@ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView;
private View mActionButtonView;
@@ -176,8 +176,7 @@
super(context, attrs, defStyleAttr, defStyleRes);
RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources();
- mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
- R.dimen.recents_task_view_shadow_rounded_corners_radius));
+ mViewBounds = createOutlineProvider();
if (config.fakeShadows) {
setBackground(new FakeShadowDrawable(res, config));
}
@@ -194,7 +193,9 @@
* Called from RecentsActivity when it is relaunched.
*/
void onReload(boolean isResumingFromVisible) {
- resetNoUserInteractionState();
+ if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
+ resetNoUserInteractionState();
+ }
if (!isResumingFromVisible) {
resetViewProperties();
}
@@ -205,6 +206,12 @@
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. */
AnimateableViewBounds getViewBounds() {
return mViewBounds;
@@ -232,7 +239,7 @@
/**
* Update the task view when the configuration changes.
*/
- void onConfigurationChanged() {
+ protected void onConfigurationChanged() {
mHeaderView.onConfigurationChanged();
}
@@ -666,10 +673,16 @@
@Override
public boolean onLongClick(View v) {
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);
- clipBounds.scale(getScaleX());
- boolean inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
+ if (!clipBounds.isEmpty()) {
+ // 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()) {
// Start listening for drag events
setClipViewInStack(false);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 691e599..c0cc83f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -290,14 +290,16 @@
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land,
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(),
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,
- 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) {
mHeaderBarHeight = headerBarHeight;
mHeaderButtonPadding = headerButtonPadding;
@@ -603,10 +605,7 @@
Constants.Metrics.DismissSourceHeaderButton);
} else if (v == mMoveTaskButton) {
TaskView tv = Utilities.findParent(this, TaskView.class);
- Rect bounds = mMoveTaskTargetStackId == FREEFORM_WORKSPACE_STACK_ID
- ? new Rect(mTaskViewRect)
- : new Rect();
- EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, bounds,
+ EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null,
mMoveTaskTargetStackId, false));
} else if (v == mAppInfoView) {
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index c46adf1..16521f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -60,6 +60,8 @@
@ViewDebug.ExportedProperty(category="recents")
private float mThumbnailScale;
private float mFullscreenThumbnailScale;
+ private boolean mSizeToFit = false;
+ private boolean mOverlayHeaderOnThumbnailActionBar = true;
private ActivityManager.TaskThumbnailInfo mThumbnailInfo;
private int mCornerRadius;
@@ -133,10 +135,12 @@
(int) (mThumbnailRect.width() * mThumbnailScale));
int thumbnailHeight = Math.min(viewHeight,
(int) (mThumbnailRect.height() * mThumbnailScale));
+
if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
- int topOffset = mTaskBar != null
- ? mTaskBar.getHeight() - mCornerRadius
- : 0;
+ int topOffset = 0;
+ if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
+ topOffset = mTaskBar.getHeight() - mCornerRadius;
+ }
// Draw the background, there will be some small overdraw with the thumbnail
if (thumbnailWidth < viewWidth) {
@@ -230,7 +234,7 @@
// If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
// and only draw the background color
mThumbnailScale = 0f;
- } else if (isStackTask) {
+ } else if (isStackTask && !mSizeToFit) {
float invThumbnailScale = 1f / mFullscreenThumbnailScale;
if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -262,6 +266,19 @@
}
}
+ /** 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. */
void updateClipToTaskBar(View taskBar) {
mTaskBar = taskBar;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
new file mode 100644
index 0000000..a029478
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
@@ -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() {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
new file mode 100644
index 0000000..6300400
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
@@ -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);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
new file mode 100644
index 0000000..65a8ee2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -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;
+ }
+}