diff options
| author | 2016-05-05 16:16:50 -0700 | |
|---|---|---|
| committer | 2016-05-06 20:51:55 +0000 | |
| commit | 27c28f8da2925fd6fdaa91603c1740447b8fc195 (patch) | |
| tree | 2890aa16581ab6934fb4c52cbd92e92bd3418959 | |
| parent | 7e2d589ce053ded6d07cc3f93297d6c32128dd3d (diff) | |
Fix exception when docking task.
- If a task fails to dock, animate the stack back to original state so
that the layout is not stuck in a "docked" state.
Bug: 28577229
Change-Id: If927b898a48cd5949764cb3b0c0798d22efd850a
8 files changed, 183 insertions, 90 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 5e4cac7c8c51..a5f3e7700cf8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -70,7 +70,6 @@ import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent; import com.android.systemui.recents.events.ui.StackViewScrolledEvent; import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent; import com.android.systemui.recents.events.ui.UserInteractionEvent; -import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 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; @@ -90,7 +89,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; /** - * The main Recents activity that is started from AlternateRecentsComponent. + * The main Recents activity that is started from RecentsComponent. */ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener { @@ -760,13 +759,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mIgnoreAltTabRelease = true; } - public final void onBusEvent(final DragEndEvent event) { - // Handle the case where we drop onto a dock region - if (event.dropTarget instanceof TaskStack.DockState) { - mScrimViews.animateScrimToCurrentNavBarState(false /* hasStackTasks */); - } - } - public final void onBusEvent(final DockedTopTaskEvent event) { mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener); mRecentsView.invalidate(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java new file mode 100644 index 000000000000..edd799597ea6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java @@ -0,0 +1,39 @@ +/* + * 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.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; +import com.android.systemui.recents.views.TaskView; + +/** + * This event is sent whenever a drag end is cancelled because of an error. + */ +public class DragEndCancelledEvent extends EventBus.AnimatedEvent { + + public final TaskStack stack; + public final Task task; + public final TaskView taskView; + + public DragEndCancelledEvent(TaskStack stack, Task task, TaskView taskView) { + this.stack = stack; + this.task = task; + this.taskView = taskView; + } +} 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 46b2612ee7f0..08b52d94ad20 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -393,18 +393,19 @@ public class SystemServicesProxy { } /** Docks a task to the side of the screen and starts it. */ - public void startTaskInDockedMode(int taskId, int createMode) { - if (mIam == null) return; + public boolean startTaskInDockedMode(int taskId, int createMode) { + if (mIam == null) return false; try { - // TODO: Determine what animation we want for the incoming task final ActivityOptions options = ActivityOptions.makeBasic(); options.setDockCreateMode(createMode); options.setLaunchStackId(DOCKED_STACK_ID); mIam.startActivityFromRecents(taskId, options.toBundle()); - } catch (RemoteException e) { + return true; + } catch (RemoteException | IllegalArgumentException e) { e.printStackTrace(); } + return false; } /** Docks an already resumed task to the side of the screen. */ 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 f6cc12bebb12..9fb8bd557b90 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -226,6 +226,18 @@ public class Utilities { } /** + * Sets the given {@link View}'s frame from its current translation. + */ + public static void setViewFrameFromTranslation(View v) { + RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + taskViewRect.offset(v.getTranslationX(), v.getTranslationY()); + v.setTranslationX(0); + v.setTranslationY(0); + v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top, + (int) taskViewRect.right, (int) taskViewRect.bottom); + } + + /** * Returns a view stub for the given view id. */ public static ViewStub findViewStubById(View v, int stubId) { 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 fd27b4f1fc27..a8939100f66d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -64,6 +64,7 @@ import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; @@ -510,45 +511,43 @@ public class RecentsView extends FrameLayout { // We translated the view but we need to animate it back from the current layout-space // rect to its final layout-space rect - int x = (int) event.taskView.getTranslationX(); - int y = (int) event.taskView.getTranslationY(); - Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(), - event.taskView.getRight(), event.taskView.getBottom()); - taskViewRect.offset(x, y); - event.taskView.setTranslationX(0); - event.taskView.setTranslationY(0); - event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top, - taskViewRect.right, taskViewRect.bottom); - - final OnAnimationStartedListener startedListener = new OnAnimationStartedListener() { - @Override - public void onAnimationStarted() { - 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 */); - } - }; + Utilities.setViewFrameFromTranslation(event.taskView); // Dock the task and launch it SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode); - final Rect taskRect = getTaskRect(event.taskView); - IAppTransitionAnimationSpecsFuture future = mTransitionHelper.getAppTransitionFuture( - new AnimationSpecComposer() { - @Override - public List<AppTransitionAnimationSpec> composeSpecs() { - return mTransitionHelper.composeDockAnimationSpec( - event.taskView, taskRect); - } - }); - ssp.overridePendingAppTransitionMultiThumbFuture(future, - mTransitionHelper.wrapStartedListener(startedListener), - true /* scaleUp */); - - MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, - event.task.getTopComponent().flattenToShortString()); + if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) { + final OnAnimationStartedListener startedListener = + new OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + 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 */); + } + }; + + final Rect taskRect = getTaskRect(event.taskView); + IAppTransitionAnimationSpecsFuture future = + mTransitionHelper.getAppTransitionFuture( + new AnimationSpecComposer() { + @Override + public List<AppTransitionAnimationSpec> composeSpecs() { + return mTransitionHelper.composeDockAnimationSpec( + event.taskView, taskRect); + } + }); + ssp.overridePendingAppTransitionMultiThumbFuture(future, + mTransitionHelper.wrapStartedListener(startedListener), + true /* scaleUp */); + + MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, + event.task.getTopComponent().flattenToShortString()); + } else { + EventBus.getDefault().send(new DragEndCancelledEvent(mStack, event.task, + event.taskView)); + } } else { // Animate the overlay alpha back to 0 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, @@ -565,6 +564,12 @@ public class RecentsView extends FrameLayout { } } + public final void onBusEvent(final DragEndCancelledEvent event) { + // Animate the overlay alpha back to 0 + updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, + true /* animateAlpha */, false /* animateBounds */); + } + private Rect getTaskRect(TaskView taskView) { int[] location = taskView.getLocationOnScreen(); int viewX = location[0]; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index 995f9f74a2f0..8f784b832e4c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -28,6 +28,9 @@ import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimatio import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.model.TaskStack; /** Manages the scrims for the various system bars. */ public class SystemBarScrimViews { @@ -154,10 +157,22 @@ public class SystemBarScrimViews { animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0); } + public final void onBusEvent(final DragEndEvent event) { + // Hide the nav bar scrims once we drop to a dock region + if (event.dropTarget instanceof TaskStack.DockState) { + animateScrimToCurrentNavBarState(false /* hasStackTasks */); + } + } + + public final void onBusEvent(final DragEndCancelledEvent event) { + // Restore the scrims to the normal state + animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0); + } + /** * Animates the scrim to match the state of the current nav bar. */ - public void animateScrimToCurrentNavBarState(boolean hasStackTasks) { + private void animateScrimToCurrentNavBarState(boolean hasStackTasks) { boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks); if (mHasNavBarScrim != hasNavBarScrim) { AnimationProps animation = hasNavBarScrim 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 773e5875b04f..3d0de1cab1e9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -79,6 +79,7 @@ import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEv 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.DragEndCancelledEvent; 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; @@ -640,19 +641,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** - * @see #relayoutTaskViews(AnimationProps, boolean) + * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean) */ public void relayoutTaskViews(AnimationProps animation) { - relayoutTaskViews(animation, false /* ignoreTaskOverrides */); + relayoutTaskViews(animation, null /* animationOverrides */, + false /* ignoreTaskOverrides */); } /** * Relayout the the visible {@link TaskView}s to their current transforms as specified by the * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any * animations that are current running on those task views, and will ensure that the children - * {@link TaskView}s will match the set of visible tasks in the stack. + * {@link TaskView}s will match the set of visible tasks in the stack. If a {@link Task} has + * an animation provided in {@param animationOverrides}, that will be used instead. */ - private void relayoutTaskViews(AnimationProps animation, boolean ignoreTaskOverrides) { + private void relayoutTaskViews(AnimationProps animation, + ArrayMap<Task, AnimationProps> animationOverrides, + boolean ignoreTaskOverrides) { // If we had a deferred animation, cancel that cancelDeferredTaskViewLayoutAnimation(); @@ -665,13 +670,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int taskViewCount = taskViews.size(); for (int i = 0; i < taskViewCount; i++) { TaskView tv = taskViews.get(i); - int taskIndex = mStack.indexOfStackTask(tv.getTask()); + Task task = tv.getTask(); + int taskIndex = mStack.indexOfStackTask(task); TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex); - if (mIgnoreTasks.contains(tv.getTask().key)) { + if (mIgnoreTasks.contains(task.key)) { continue; } + if (animationOverrides != null && animationOverrides.containsKey(task)) { + animation = animationOverrides.get(task); + } + updateTaskViewToTransform(tv, transform, animation); } } @@ -829,6 +839,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + /** + * Updates the stack layout to its stable places. + */ + private void updateLayoutToStableBounds() { + mWindowRect.set(mStableWindowRect); + mStackBounds.set(mStableStackBounds); + mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets); + mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds, + TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack)); + updateLayoutAlgorithm(true /* boundScroll */); + } + /** Returns the scroller. */ public TaskStackViewScroller getScroller() { return mStackScroller; @@ -1820,16 +1842,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } else { // 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 - mWindowRect.set(mStableWindowRect); - mStackBounds.set(mStableStackBounds); removeIgnoreTask(event.task); - mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets); - mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds, - TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack)); - updateLayoutAlgorithm(true /* boundScroll */); + updateLayoutToStableBounds(); addIgnoreTask(event.task); } - relayoutTaskViews(animation, ignoreTaskOverrides); + relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides); } public final void onBusEvent(final DragEndEvent event) { @@ -1867,30 +1884,38 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal }); } - // We translated the view but we need to animate it back from the current layout-space rect - // to its final layout-space rect - int x = (int) event.taskView.getTranslationX(); - int y = (int) event.taskView.getTranslationY(); - Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(), - event.taskView.getRight(), event.taskView.getBottom()); - taskViewRect.offset(x, y); - event.taskView.setTranslationX(0); - event.taskView.setTranslationY(0); - event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top, - taskViewRect.right, taskViewRect.bottom); + // Restore the task, so that relayout will apply to it below + removeIgnoreTask(event.task); - // Animate the non-drag TaskViews back into position - mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(), - mTmpTransform, null); - event.getAnimationTrigger().increment(); - relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION, + // Convert the dragging task view back to its final layout-space rect + Utilities.setViewFrameFromTranslation(event.taskView); + + // Animate all the tasks into place + ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>(); + animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION, + Interpolators.FAST_OUT_SLOW_IN, + event.getAnimationTrigger().decrementOnAnimationEnd())); + relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN)); + event.getAnimationTrigger().increment(); + } - // Animate the drag TaskView back into position - updateTaskViewToTransform(event.taskView, mTmpTransform, - new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN, - event.getAnimationTrigger().decrementOnAnimationEnd())); + public final void onBusEvent(final DragEndCancelledEvent event) { + // Restore the pre-drag task stack bounds, including the dragging task view removeIgnoreTask(event.task); + updateLayoutToStableBounds(); + + // Convert the dragging task view back to its final layout-space rect + Utilities.setViewFrameFromTranslation(event.taskView); + + // Animate all the tasks into place + ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>(); + animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION, + Interpolators.FAST_OUT_SLOW_IN, + event.getAnimationTrigger().decrementOnAnimationEnd())); + relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION, + Interpolators.FAST_OUT_SLOW_IN)); + event.getAnimationTrigger().increment(); } public final void onBusEvent(IterateRecentsEvent event) { 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 c1e7e0403c88..612c41d07a04 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -49,6 +49,7 @@ import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; import com.android.systemui.recents.events.ui.TaskViewDismissedEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; @@ -695,15 +696,18 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks public final void onBusEvent(DragEndEvent event) { if (!(event.dropTarget instanceof TaskStack.DockState)) { - event.addPostAnimationCallback(new Runnable() { - @Override - public void run() { - // Animate the drag view back from where it is, to the view location, then after - // it returns, update the clip state - setClipViewInStack(true); - } + event.addPostAnimationCallback(() -> { + // Reset the clip state for the drag view after the end animation completes + setClipViewInStack(true); }); } EventBus.getDefault().unregister(this); } + + public final void onBusEvent(DragEndCancelledEvent event) { + // Reset the clip state for the drag view after the cancel animation completes + event.addPostAnimationCallback(() -> { + setClipViewInStack(true); + }); + } } |