From eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 4 Nov 2015 10:50:28 -0800 Subject: [PATCH] Adding dnd to/from freeform workspaces. Change-Id: I5b75fe5a942e9a2e344e62229e860b5108f9872e --- .../recents/RecentsConfiguration.java | 3 +- .../android/systemui/recents/RecentsImpl.java | 4 +- .../systemui/recents/events/EventBus.java | 6 +- ...t.java => DragDropTargetChangedEvent.java} | 12 +- .../events/ui/dragndrop/DragEndEvent.java | 10 +- .../DragStartInitializeDropTargetsEvent.java | 36 +++++ .../recents/misc/SystemServicesProxy.java | 18 ++- .../android/systemui/recents/model/Task.java | 16 +- .../systemui/recents/model/TaskStack.java | 93 ++++++++---- .../systemui/recents/views/DropTarget.java | 29 ++++ .../FreeformWorkspaceLayoutAlgorithm.java | 5 + .../systemui/recents/views/RecentsView.java | 84 +++++++---- .../views/RecentsViewTouchHandler.java | 59 +++++--- .../views/TaskStackLayoutAlgorithm.java | 45 ++---- .../systemui/recents/views/TaskStackView.java | 142 ++++++++++++++++-- .../systemui/recents/views/TaskView.java | 52 ++++--- 16 files changed, 458 insertions(+), 156 deletions(-) rename packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/{DragDockStateChangedEvent.java => DragDropTargetChangedEvent.java} (70%) create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index e2f20fdefbb37..a73f3234ed44a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -110,7 +110,8 @@ public class RecentsConfiguration { void update(Context context, SystemServicesProxy ssp, Rect windowRect) { // Only update resources that can change after the first load, either through developer // settings or via multi window - lockToAppEnabled = ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0; + lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() && + ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0; hasDockedTasks = ssp.hasDockedTask(); // Recompute some values based on the given state, since we can not rely on the resource diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 0e11f02a34ec9..c759f16d7a103 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -511,7 +511,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub reloadHeaderBarLayout(false /* tryAndBindSearchWidget */); // Update the destination rect - mDummyStackView.updateMinMaxScrollForStack(stack); + mDummyStackView.updateLayoutForStack(stack); final Task toTask = new Task(); final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView, topTask.id, toTask); @@ -694,7 +694,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub TaskStack stack = sInstanceLoadPlan.getTaskStack(); // Prepare the dummy stack for the transition - mDummyStackView.updateMinMaxScrollForStack(stack); + mDummyStackView.updateLayoutForStack(stack); TaskStackLayoutAlgorithm.VisibilityReport stackVr = mDummyStackView.computeStackVisibilityReport(); boolean hasRecentTasks = stack.getTaskCount() > 0; diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java index deae4c8d5ff9a..b091f050a340b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java @@ -549,7 +549,7 @@ public class EventBus extends BroadcastReceiver { InvocationTargetException| InstantiationException| IllegalAccessException e) { - Log.e(TAG, "Failed to create InterprocessEvent", e); + Log.e(TAG, "Failed to create InterprocessEvent", e.getCause()); } } @@ -746,9 +746,9 @@ public class EventBus extends BroadcastReceiver { Log.e(TAG, "Failed to deliver event to null subscriber"); } } catch (IllegalAccessException e) { - Log.e(TAG, "Failed to invoke method", e); + Log.e(TAG, "Failed to invoke method", e.getCause()); } catch (InvocationTargetException e) { - throw new RuntimeException(e.getCause()); + Log.e(TAG, "Failed to invoke method", e.getCause()); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java similarity index 70% rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java rename to packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java index f2c3c338383e3..21321f297159e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java @@ -18,18 +18,18 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.views.DropTarget; /** - * This event is sent when a user drag enters or exits a dock region. + * This event is sent when a user drags in/out of a drop target. */ -public class DragDockStateChangedEvent extends EventBus.Event { +public class DragDropTargetChangedEvent extends EventBus.Event { public final Task task; - public final TaskStack.DockState dockState; + public final DropTarget dropTarget; - public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) { + public DragDropTargetChangedEvent(Task task, DropTarget dropTarget) { this.task = task; - this.dockState = dockState; + this.dropTarget = dropTarget; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java index 827998d8529f6..957da9441ec62 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java @@ -19,8 +19,8 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.views.DragView; +import com.android.systemui.recents.views.DropTarget; import com.android.systemui.recents.views.TaskView; /** @@ -31,15 +31,15 @@ public class DragEndEvent extends EventBus.Event { public final Task task; public final TaskView taskView; public final DragView dragView; - public final TaskStack.DockState dockState; + public final DropTarget dropTarget; public final ReferenceCountedTrigger postAnimationTrigger; - public DragEndEvent(Task task, TaskView taskView, DragView dragView, - TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) { + public DragEndEvent(Task task, TaskView taskView, DragView dragView, DropTarget dropTarget, + ReferenceCountedTrigger postAnimationTrigger) { this.task = task; this.taskView = taskView; this.dragView = dragView; - this.dockState = dockState; + this.dropTarget = dropTarget; this.postAnimationTrigger = postAnimationTrigger; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java new file mode 100644 index 0000000000000..b450b1fdcaecd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.ui.dragndrop; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.views.RecentsViewTouchHandler; + +/** + * This event is sent by the drag manager when it requires drop targets to register themselves for + * the current drag gesture. + */ +public class DragStartInitializeDropTargetsEvent extends EventBus.Event { + + public final Task task; + public final RecentsViewTouchHandler handler; + + public DragStartInitializeDropTargetsEvent(Task task, RecentsViewTouchHandler handler) { + this.task = task; + this.handler = handler; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 2fe5e988b3e60..c2f23648fd354 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -103,6 +103,7 @@ public class SystemServicesProxy { Display mDisplay; String mRecentsPackage; ComponentName mAssistComponent; + boolean mHasFreeformWorkspaceSupport; Bitmap mDummyIcon; int mDummyThumbnailWidth; @@ -123,6 +124,8 @@ public class SystemServicesProxy { mUm = UserManager.get(context); mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); + mHasFreeformWorkspaceSupport = false && mPm.hasSystemFeature( + PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT); // Get the dummy thumbnail width/heights Resources res = context.getResources(); @@ -241,7 +244,7 @@ public class SystemServicesProxy { public boolean hasFreeformWorkspaceSupport() { if (mPm == null) return false; - return mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT); + return mHasFreeformWorkspaceSupport; } /** Returns whether the recents is currently running */ @@ -411,6 +414,19 @@ public class SystemServicesProxy { return thumbnail; } + /** + * Moves a task into another stack. + */ + public void moveTaskToStack(int taskId, int stackId) { + if (mIam == null) return; + + try { + mIam.positionTaskInStack(taskId, stackId, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + /** Moves a task to the front with the specified activity options. */ public void moveTaskToFront(int taskId, ActivityOptions opts) { if (mAm == null) return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 51a1ebc579e41..12bd556b8655c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -22,6 +22,8 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; import java.util.Objects; @@ -165,7 +167,9 @@ public class Task { this.group = group; } - /** Updates the stack id of this task. */ + /** + * Updates the stack id of this task. + */ public void setStackId(int stackId) { key.stackId = stackId; if (mCb != null) { @@ -173,10 +177,12 @@ public class Task { } } + /** + * Returns whether this task is on the freeform task stack. + */ public boolean isFreeformTask() { - // Temporarily disable: - return false; - // return SystemServicesProxy.isFreeformStack(key.stackId); + SystemServicesProxy ssp = Recents.getSystemServices(); + return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId); } /** Notifies the callback listeners that this task has been loaded */ @@ -210,7 +216,7 @@ public class Task { if (group != null) { groupAffiliation = Integer.toString(group.affiliation); } - return "Task (" + groupAffiliation + "): " + key.getComponent().getPackageName() + + return "Task (" + groupAffiliation + "): " + key + " [" + super.toString() + "]"; } } 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 a96fe98bd8177..495c8fd2bc5d6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -17,18 +17,21 @@ package com.android.systemui.recents.model; import android.animation.ObjectAnimator; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.NamedCounter; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.recents.views.DropTarget; import java.util.ArrayList; import java.util.Collections; @@ -40,6 +43,8 @@ import java.util.Random; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; /** @@ -54,9 +59,13 @@ interface TaskFilter { * A list of filtered tasks. */ class FilteredTaskList { - ArrayList mTasks = new ArrayList(); - ArrayList mFilteredTasks = new ArrayList(); - HashMap mTaskIndices = new HashMap(); + + private static final String TAG = "FilteredTaskList"; + private static final boolean DEBUG = true; + + ArrayList mTasks = new ArrayList<>(); + ArrayList mFilteredTasks = new ArrayList<>(); + HashMap mTaskIndices = new HashMap<>(); TaskFilter mFilter; /** Sets the task filter, saving the current touch state */ @@ -93,6 +102,25 @@ class FilteredTaskList { updateFilteredTasks(); } + /** + * Moves the given task. + */ + public void moveTaskToStack(Task task, int insertIndex, int newStackId) { + int taskIndex = indexOf(task); + if (taskIndex != insertIndex) { + mTasks.remove(taskIndex); + if (taskIndex < insertIndex) { + insertIndex--; + } + mTasks.add(insertIndex, task); + } + + // Update the stack id now, after we've moved the task, and before we update the + // filtered tasks + task.setStackId(newStackId); + updateFilteredTasks(); + } + /** Sets the list of tasks */ void set(List tasks) { mTasks.clear(); @@ -187,16 +215,21 @@ public class TaskStack { } - public enum DockState { - NONE(-1, 96, null, null, null), + public enum DockState implements DropTarget { + NONE(-1, 96, null, null), LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, - new RectF(0, 0, 0.3f, 1), new RectF(0, 0, 0.3f, 1), new RectF(0.7f, 0, 1, 1)), + new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1)), TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, 192, - new RectF(0, 0, 1, 0.3f), new RectF(0, 0, 1, 0.3f), new RectF(0, 0.7f, 1, 1)), + new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f)), RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, - new RectF(0.7f, 0, 1, 1), new RectF(0.7f, 0, 1, 1), new RectF(0, 0, 0.3f, 1)), + new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1)), BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192, - new RectF(0, 0.7f, 1, 1), new RectF(0, 0.7f, 1, 1), new RectF(0, 0, 1, 0.3f)); + new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1)); + + @Override + public boolean acceptsDrop(int x, int y, int width, int height) { + return touchAreaContainsPoint(width, height, x, y); + } // Represents the view state of this dock state public class ViewState { @@ -229,20 +262,17 @@ public class TaskStack { public final ViewState viewState; private final RectF dockArea; private final RectF touchArea; - private final RectF stackArea; /** * @param createMode used to pass to ActivityManager to dock the task * @param touchArea the area in which touch will initiate this dock state - * @param stackArea the area for the stack if a task is docked + * @param dockArea the visible dock area */ - DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea, - RectF stackArea) { + DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea) { this.createMode = createMode; this.viewState = new ViewState(dockAreaAlpha); this.dockArea = dockArea; this.touchArea = touchArea; - this.stackArea = stackArea; } /** @@ -264,18 +294,6 @@ public class TaskStack { return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height), (int) (dockArea.right * width), (int) (dockArea.bottom * height)); } - - /** - * Returns the stack bounds with the given {@param width} and {@param height}. - */ - public Rect getStackBounds(Rect stackRect) { - int width = stackRect.width(); - int height = stackRect.height(); - return new Rect((int) (stackRect.left + stackArea.left * width), - (int) (stackRect.top + stackArea.top * height), - (int) (stackRect.left + (stackArea.right - stackArea.left) * width), - (int) (stackRect.top + (stackArea.bottom - stackArea.top) * height)); - } } // The task offset to apply to a task id as a group affiliation @@ -308,6 +326,29 @@ public class TaskStack { } } + /** + * Moves the given task to either the front of the freeform workspace or the stack. + */ + public void moveTaskToStack(Task task, int newStackId) { + // Find the index to insert into + ArrayList taskList = mTaskList.getTasks(); + int taskCount = taskList.size(); + if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) { + // Insert freeform tasks at the front + mTaskList.moveTaskToStack(task, taskCount, newStackId); + } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) { + // Insert after the first stacked task + int insertIndex = 0; + for (int i = taskCount - 1; i >= 0; i--) { + if (!taskList.get(i).isFreeformTask()) { + insertIndex = i + 1; + break; + } + } + mTaskList.moveTaskToStack(task, insertIndex, newStackId); + } + } + /** Does the actual work associated with removing the task. */ void removeTaskImpl(Task t) { // Remove the task from the list diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java new file mode 100644 index 0000000000000..8ae00a7eae824 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 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; + +/** + * Represents a drop target for a drag gesture. + */ +public interface DropTarget { + + /** + * Returns whether this target can accept this drop. The x,y are relative to the top level + * RecentsView, and the width/height are of the RecentsView. + */ + boolean acceptsDrop(int x, int y, int width, int height); +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java index a97a2a800ac4a..765f6861d7a1e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java @@ -111,6 +111,11 @@ public class FreeformWorkspaceLayoutAlgorithm { Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale); transformOut.visible = true; transformOut.p = 0; + + if (DEBUG) { + Log.d(TAG, "getTransform: " + task.key + ", " + transformOut); + } + return transformOut; } return null; 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 d72e50e0c5dc7..24871d47dfe2c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -16,7 +16,6 @@ package com.android.systemui.recents.views; -import android.app.ActivityManager; import android.app.ActivityOptions; import android.content.Context; import android.graphics.Bitmap; @@ -51,7 +50,7 @@ import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; -import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -336,9 +335,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } if (mDragView != null) { + Rect taskRect = mTaskStackView.mLayoutAlgorithm.mTaskRect; mDragView.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + MeasureSpec.makeMeasureSpec(taskRect.width(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(taskRect.height(), MeasureSpec.AT_MOST)); } setMeasuredDimension(width, height); @@ -787,52 +787,76 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskStack.DockState.NONE.viewState.dockAreaAlpha); } - public final void onBusEvent(DragDockStateChangedEvent event) { - if (event.dockState == TaskStack.DockState.NONE) { + public final void onBusEvent(DragDropTargetChangedEvent event) { + if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), TaskStack.DockState.NONE.viewState.dockAreaAlpha); } else { - updateVisibleDockRegions(new TaskStack.DockState[] {event.dockState}, -1); + final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; + updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, -1); } } public final void onBusEvent(final DragEndEvent event) { - event.postAnimationTrigger.increment(); - event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + final Runnable cleanUpRunnable = new Runnable() { @Override public void run() { // Remove the drag view removeView(mDragView); mDragView = null; - updateVisibleDockRegions(null, -1); - - // Dock the new task if we are hovering over a valid dock state - if (event.dockState != TaskStack.DockState.NONE) { - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startTaskInDockedMode(event.task.key.id, event.dockState.createMode); - launchTask(event.task, null, INVALID_STACK_ID); - } } - }); - if (event.dockState == TaskStack.DockState.NONE) { - // Animate the alpha back to what it was before - Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds(); - int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2; - int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2; + }; + + // Animate the overlay alpha back to 0 + updateVisibleDockRegions(null, -1); + + if (event.dropTarget == null) { + // No drop targets for hit, so just animate the task back to its place + event.postAnimationTrigger.increment(); + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + cleanUpRunnable.run(); + } + }); + // Animate the task back to where it was before then clean up afterwards + TaskViewTransform taskTransform = new TaskViewTransform(); + TaskStackLayoutAlgorithm layoutAlgorithm = mTaskStackView.getStackAlgorithm(); + layoutAlgorithm.getStackTransform(event.task, + mTaskStackView.getScroller().getStackScroll(), taskTransform, null); event.dragView.animate() - .scaleX(1f) - .scaleY(1f) - .translationX(left + event.taskView.getTranslationX()) - .translationY(top + event.taskView.getTranslationY()) + .scaleX(taskTransform.scale) + .scaleY(taskTransform.scale) + .translationX((layoutAlgorithm.mTaskRect.left - event.dragView.getLeft()) + + taskTransform.translationX) + .translationY((layoutAlgorithm.mTaskRect.top - event.dragView.getTop()) + + taskTransform.translationY) .setDuration(175) .setInterpolator(mFastOutSlowInInterpolator) .withEndAction(event.postAnimationTrigger.decrementAsRunnable()) .start(); - // Animate the overlay alpha back to 0 - updateVisibleDockRegions(null, -1); + } else if (event.dropTarget instanceof TaskStack.DockState) { + final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; + + // For now, just remove the drag view and the original task + // TODO: Animate the task to the drop target rect before launching it above + cleanUpRunnable.run(); + + // Dock the task and launch it + SystemServicesProxy ssp = Recents.getSystemServices(); + ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode); + launchTask(event.task, null, INVALID_STACK_ID); + } else { - event.postAnimationTrigger.decrement(); + // We dropped on another drop target, so just add the cleanup to the post animation + // trigger + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + cleanUpRunnable.run(); + } + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java index f3a43905a823d..c7edc92fc207c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -22,13 +22,16 @@ import android.view.MotionEvent; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; +import java.util.ArrayList; + /** * Represents the dock regions for each orientation. @@ -52,7 +55,10 @@ class DockRegion { /** * Handles touch events for a RecentsView. */ -class RecentsViewTouchHandler { +public class RecentsViewTouchHandler { + + private static final String TAG = "RecentsViewTouchHandler"; + private static final boolean DEBUG = false; private RecentsView mRv; @@ -62,12 +68,21 @@ class RecentsViewTouchHandler { private Point mDownPos = new Point(); private boolean mDragging; - private TaskStack.DockState mLastDockState = TaskStack.DockState.NONE; + + private DropTarget mLastDropTarget; + private ArrayList mDropTargets = new ArrayList<>(); public RecentsViewTouchHandler(RecentsView rv) { mRv = rv; } + /** + * Registers a new drop target for the current drag only. + */ + public void registerDropTargetForCurrentDrag(DropTarget target) { + mDropTargets.add(target); + } + /** * Returns the preferred dock states for the current orientation. */ @@ -101,11 +116,24 @@ class RecentsViewTouchHandler { mDragTask = event.task; mTaskView = event.taskView; mDragView = event.dragView; + mDropTargets.clear(); float x = mDownPos.x - mDragView.getTopLeftOffset().x; float y = mDownPos.y - mDragView.getTopLeftOffset().y; mDragView.setTranslationX(x); mDragView.setTranslationY(y); + + RecentsConfiguration config = Recents.getConfiguration(); + if (!config.hasDockedTasks) { + // Add the dock state drop targets (these take priority) + TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation(); + for (TaskStack.DockState dockState : dockStates) { + registerDropTargetForCurrentDrag(dockState); + } + } + + // Request other drop targets to register themselves + EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task, this)); } public final void onBusEvent(DragEndEvent event) { @@ -113,7 +141,7 @@ class RecentsViewTouchHandler { mDragTask = null; mTaskView = null; mDragView = null; - mLastDockState = TaskStack.DockState.NONE; + mLastDropTarget = null; } /** @@ -121,8 +149,6 @@ class RecentsViewTouchHandler { * @param ev */ private void handleTouchEvent(MotionEvent ev) { - boolean isLandscape = mRv.getResources().getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: @@ -137,20 +163,17 @@ class RecentsViewTouchHandler { float x = evX - mDragView.getTopLeftOffset().x; float y = evY - mDragView.getTopLeftOffset().y; - // Update the dock state - TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation(); - TaskStack.DockState foundDockState = TaskStack.DockState.NONE; - for (int i = dockStates.length - 1; i >= 0; i--) { - TaskStack.DockState state = dockStates[i]; - if (state.touchAreaContainsPoint(width, height, evX, evY)) { - foundDockState = state; + DropTarget currentDropTarget = null; + for (DropTarget target : mDropTargets) { + if (target.acceptsDrop((int) evX, (int) evY, width, height)) { + currentDropTarget = target; break; } } - if (mLastDockState != foundDockState) { - mLastDockState = foundDockState; - EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask, - foundDockState)); + if (mLastDropTarget != currentDropTarget) { + mLastDropTarget = currentDropTarget; + EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, + currentDropTarget)); } mDragView.setTranslationX(x); @@ -165,7 +188,7 @@ class RecentsViewTouchHandler { mRv.getContext(), null, null, null); postAnimationTrigger.increment(); EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView, - mLastDockState, postAnimationTrigger)); + mLastDropTarget, postAnimationTrigger)); postAnimationTrigger.decrement(); break; } 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 a0713d7defc0e..7f5c768d870fe 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -63,23 +63,6 @@ public class TaskStackLayoutAlgorithm { Context mContext; - /* - +-------------------+ - | SEARCH | - +-------------------+ - |+-----------------+| - || FREEFORM || - || || - || || - |+-----------------+| - | +-----------+ | - | +---------------+ | - | | | | - |+-----------------+| - || STACK || - +-------------------+ - */ - // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot. public Rect mTaskRect = new Rect(); // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height @@ -165,8 +148,8 @@ public class TaskStackLayoutAlgorithm { }, new ParametricCurve.ParametricCurveFunction() { @Override public float f(float p) { - // Don't scale when there are freeform tasks - if (mNumFreeformTasks > 0) { + SystemServicesProxy ssp = Recents.getSystemServices(); + if (ssp.hasFreeformWorkspaceSupport()) { return 1f; } @@ -196,6 +179,7 @@ public class TaskStackLayoutAlgorithm { */ public void initialize(Rect taskStackBounds) { SystemServicesProxy ssp = Recents.getSystemServices(); + RecentsConfiguration config = Recents.getConfiguration(); int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width()); int heightPadding = mContext.getResources().getDimensionPixelSize( @@ -262,9 +246,7 @@ public class TaskStackLayoutAlgorithm { * in the stack. */ void update(TaskStack stack) { - if (DEBUG) { - Log.d(TAG, "update"); - } + SystemServicesProxy ssp = Recents.getSystemServices(); // Clear the progress map mTaskProgressMap.clear(); @@ -292,10 +274,10 @@ public class TaskStackLayoutAlgorithm { mNumStackTasks = stackTasks.size(); mNumFreeformTasks = freeformTasks.size(); + float pAtBackMostTaskTop = 0; + float pAtFrontMostTaskTop = pAtBackMostTaskTop; if (!stackTasks.isEmpty()) { // Update the for each task from back to front. - float pAtBackMostTaskTop = 0; - float pAtFrontMostTaskTop = pAtBackMostTaskTop; int taskCount = stackTasks.size(); for (int i = 0; i < taskCount; i++) { Task task = stackTasks.get(i); @@ -307,7 +289,7 @@ public class TaskStackLayoutAlgorithm { if (i < (taskCount - 1)) { // Increment the peek height - float pPeek = task.group.isFrontMostTask(task) ? + float pPeek = task.group == null || task.group.isFrontMostTask(task) ? mBetweenAffiliationPOffset : mWithinAffiliationPOffset; pAtFrontMostTaskTop += pPeek; } @@ -318,10 +300,12 @@ public class TaskStackLayoutAlgorithm { // Set the stack end scroll progress to the point at which the bottom of the front-most // task is aligned to the bottom of the stack mMaxScrollP = alignToStackBottom(pAtFrontMostTaskTop, - mStackBottomPOffset + mTaskHeightPOffset); + mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ? + mTaskHalfHeightPOffset : mTaskHeightPOffset)); // Basically align the back-most task such that the last two tasks would be visible mMinScrollP = alignToStackBottom(pAtBackMostTaskTop, - mStackBottomPOffset + mTaskHeightPOffset); + mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ? + mTaskHalfHeightPOffset : mTaskHeightPOffset)); } else { // When there is a single item, then just make all the stack progresses the same mMinScrollP = mMaxScrollP = 0; @@ -379,7 +363,7 @@ public class TaskStackLayoutAlgorithm { if (progress < 0) { break; } - boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task); + boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task); if (isFrontMostTaskInGroup) { float scaleAtP = sCurve.pToScale(progress); int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2); @@ -432,12 +416,13 @@ public class TaskStackLayoutAlgorithm { /** Update/get the transform */ public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) { + SystemServicesProxy ssp = Recents.getSystemServices(); - if (mNumStackTasks == 1) { + if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { // Center the task in the stack, changing the scale will not follow the curve, but just // modulate some values directly float pTaskRelative = mMinScrollP - stackScroll; - float scale = (mNumFreeformTasks > 0) ? 1f : SINGLE_TASK_SCALE; + float scale = ssp.hasFreeformWorkspaceSupport() ? 1f : SINGLE_TASK_SCALE; int topOffset = (mCurrentStackRect.top - mTaskRect.top) + (mCurrentStackRect.height() - mTaskRect.height()) / 2; transformOut.scale = scale; 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 14d75a85000db..3f1a3d2278cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -30,6 +30,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.recents.Constants; @@ -42,6 +44,10 @@ import com.android.systemui.recents.events.activity.PackagesChangedEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; import com.android.systemui.recents.events.ui.UserInteractionEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent; import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent; import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent; import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent; @@ -58,6 +64,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; @@ -110,8 +118,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal LayoutInflater mInflater; boolean mLayersDisabled; + Interpolator mFastOutSlowInInterpolator; + // A convenience update listener to request updating clipping of tasks - ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener = + private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -119,6 +129,21 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } }; + // The drop targets for a task drag + private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() { + @Override + public boolean acceptsDrop(int x, int y, int width, int height) { + return mLayoutAlgorithm.mFreeformRect.contains(x, y); + } + }; + + private DropTarget mStackDropTarget = new DropTarget() { + @Override + public boolean acceptsDrop(int x, int y, int width, int height) { + return mLayoutAlgorithm.mCurrentStackRect.contains(x, y); + } + }; + public TaskStackView(Context context, TaskStack stack) { super(context); // Set the stack first @@ -130,6 +155,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm); mStackScroller.setCallbacks(this); mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); int taskBarDismissDozeDelaySeconds = getResources().getInteger( R.integer.recents_task_bar_dismiss_delay_seconds); @@ -390,7 +417,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Pick up all the freeform tasks int firstVisStackIndex = isValidVisibleStackRange ? visibleStackRange[0] : 0; - for (int i = mStack.getTaskCount() - 1; i > firstVisStackIndex; i--) { + for (int i = mStack.getTaskCount() - 1; i >= firstVisStackIndex; i--) { Task task = tasks.get(i); if (!task.isFreeformTask()) { continue; @@ -411,6 +438,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Animate the task into place tv.updateViewPropertiesToTaskTransform(transform, mStackViewsAnimationDuration, mRequestUpdateClippingListener); + + // Reattach it in the right z order + detachViewFromParent(tv); + int insertIndex = -1; + int taskIndex = mStack.indexOfTask(task); + taskViews = getTaskViews(); + taskViewCount = taskViews.size(); + for (int j = 0; j < taskViewCount; j++) { + Task tvTask = taskViews.get(j).getTask(); + if (taskIndex < mStack.indexOfTask(tvTask)) { + insertIndex = j; + break; + } + } + attachViewToParent(tv, insertIndex, tv.getLayoutParams()); + + // Update the task views list after adding the new task view + updateTaskViewsList(); } // Pick up all the newly visible children and update all the existing children @@ -514,7 +559,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** Updates the min and max virtual scroll bounds */ - void updateMinMaxScroll(boolean boundScrollToNewMinMax) { + void updateLayout(boolean boundScrollToNewMinMax) { // Compute the min and max scroll values mLayoutAlgorithm.update(mStack); @@ -700,21 +745,21 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mLayoutAlgorithm.initialize(taskStackBounds); // Update the scroll bounds - updateMinMaxScroll(false); + updateLayout(false); } /** - * This is ONLY used from AlternateRecentsComponent to update the dummy stack view for purposes + * This is ONLY used from the Recents component to update the dummy stack view for purposes * of getting the task rect to animate to. */ - public void updateMinMaxScrollForStack(TaskStack stack) { + public void updateLayoutForStack(TaskStack stack) { mStack = stack; - updateMinMaxScroll(false); + updateLayout(false); } /** * Computes the maximum number of visible tasks and thumbnails. Requires that - * updateMinMaxScrollForStack() is called first. + * updateLayoutForStack() is called first. */ public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() { return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks()); @@ -1005,7 +1050,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Update the min/max scroll and animate other task views into their new positions - updateMinMaxScroll(true); + updateLayout(true); if (wasFrontMostTask) { // Since the max scroll progress is offset from the bottom of the stack, just scroll @@ -1031,7 +1076,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Update the min/max scroll and animate other task views into their new positions - updateMinMaxScroll(true); + updateLayout(true); // Animate all the tasks into place requestSynchronizeStackViewsWithModel(200); @@ -1091,7 +1136,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks()); // Scroll the item to the top of the stack (sans-peek) rect so that we can see it better - updateMinMaxScroll(false); + updateLayout(false); float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight(); setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight)); boundScrollRaw(); @@ -1120,7 +1165,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks()); // Restore the stashed scroll - updateMinMaxScroll(false); + updateLayout(false); setStackScrollRaw(mStashedScroll); boundScrollRaw(); @@ -1308,6 +1353,79 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + public final void onBusEvent(DragStartEvent event) { + if (event.task.isFreeformTask()) { + // Animate to the front of the stack + mStackScroller.animateScroll(mStackScroller.getStackScroll(), + mLayoutAlgorithm.mInitialScrollP, null); + } + } + + public final void onBusEvent(DragStartInitializeDropTargetsEvent event) { + SystemServicesProxy ssp = Recents.getSystemServices(); + if (ssp.hasFreeformWorkspaceSupport()) { + event.handler.registerDropTargetForCurrentDrag(mStackDropTarget); + event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget); + } + } + + public final void onBusEvent(DragDropTargetChangedEvent event) { + // TODO: Animate the freeform workspace background etc. + } + + public final void onBusEvent(final DragEndEvent event) { + if (event.dropTarget != mFreeformWorkspaceDropTarget && + event.dropTarget != mStackDropTarget) { + return; + } + if (event.task.isFreeformTask() && event.dropTarget == mFreeformWorkspaceDropTarget) { + // TODO: Animate back into view + return; + } + if (!event.task.isFreeformTask() && event.dropTarget == mStackDropTarget) { + // TODO: Animate back into view + return; + } + + // Move the task to the right position in the stack (ie. the front of the stack if freeform + // or the front of the stack if fullscreen). Note, we MUST move the tasks before we update + // their stack ids, otherwise, the keys will have changed. + if (event.dropTarget == mFreeformWorkspaceDropTarget) { + mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID); + updateLayout(true); + } else if (event.dropTarget == mStackDropTarget) { + mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID); + updateLayout(true); + } + + event.postAnimationTrigger.increment(); + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + SystemServicesProxy ssp = Recents.getSystemServices(); + ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId); + } + }); + + // Animate the drag view to the new position + mLayoutAlgorithm.getStackTransform(event.task, mStackScroller.getStackScroll(), + mTmpTransform, null); + event.dragView.animate() + .scaleX(mTmpTransform.scale) + .scaleY(mTmpTransform.scale) + .translationX((mLayoutAlgorithm.mTaskRect.left - event.dragView.getLeft()) + + mTmpTransform.translationX) + .translationY((mLayoutAlgorithm.mTaskRect.top - event.dragView.getTop()) + + mTmpTransform.translationY) + .setDuration(175) + .setInterpolator(mFastOutSlowInInterpolator) + .withEndAction(event.postAnimationTrigger.decrementAsRunnable()) + .start(); + + // Animate the other views into place + requestSynchronizeStackViewsWithModel(175); + } + /** * Removes the task from the stack, and updates the focus to the next task in the stack if the * removed TaskView was focused. 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 57cb599162c58..dd1474d9633e5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -51,6 +51,7 @@ import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; import com.android.systemui.statusbar.phone.PhoneStatusBar; /* A task view */ @@ -708,6 +709,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, @Override public void onTaskDataLoaded() { + SystemServicesProxy ssp = Recents.getSystemServices(); RecentsConfiguration config = Recents.getConfiguration(); if (mThumbnailView != null && mHeaderView != null) { // Bind each of the views to the new task data @@ -715,7 +717,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mHeaderView.rebindToTask(mTask); // Rebind any listeners mActionButtonView.setOnClickListener(this); - setOnLongClickListener(config.hasDockedTasks ? null : this); + + // Only enable long-click if we have a freeform workspace to drag to/from, or if we + // aren't already docked + if (ssp.hasFreeformWorkspaceSupport() || !config.hasDockedTasks) { + setOnLongClickListener(this); + } else { + setOnLongClickListener(null); + } } mTaskDataLoaded = true; } @@ -759,15 +768,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // Start listening for drag events setClipViewInStack(false); - final int width = (int) (getScaleX() * getWidth()); - final int height = (int) (getScaleY() * getHeight()); + final float finalScale = getScaleX() * 1.05f; + final int width = getWidth(); + final int height = getHeight(); Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(dragBitmap); - c.scale(getScaleX(), getScaleY()); mThumbnailView.draw(c); mHeaderView.draw(c); c.setBitmap(null); + // The downTouchPos is relative to the currently transformed TaskView, but we will be + // dragging a copy of the full task view, which makes it easier for us to animate them + // when the user drops + mDownTouchPos.x += ((1f - getScaleX()) * width) / 2; + mDownTouchPos.y += ((1f - getScaleY()) * height) / 2; + // Initiate the drag final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos); dragView.setOutlineProvider(new ViewOutlineProvider() { @@ -776,6 +791,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, outline.setRect(0, 0, width, height); } }); + dragView.setScaleX(getScaleX()); + dragView.setScaleY(getScaleY()); dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { @@ -785,8 +802,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, dragView.setElevation(getElevation()); dragView.setTranslationZ(getTranslationZ()); dragView.animate() - .scaleX(1.05f) - .scaleY(1.05f) + .scaleX(finalScale) + .scaleY(finalScale) .setDuration(175) .setInterpolator(mFastOutSlowInInterpolator) .start(); @@ -807,18 +824,19 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /**** Events ****/ public final void onBusEvent(DragEndEvent event) { - event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { - @Override - public void run() { - // If docked state == null: - // Animate the drag view back from where it is, to the view location, then after it returns, - // update the clip state - setClipViewInStack(true); + if (!(event.dropTarget instanceof TaskStack.DockState)) { + event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + // Show this task view + setVisibility(View.VISIBLE); - // Show this task view - setVisibility(View.VISIBLE); - } - }); + // Animate the drag view back from where it is, to the view location, then after + // it returns, update the clip state + setClipViewInStack(true); + } + }); + } EventBus.getDefault().unregister(this); } }