When long pressing recents and already docked, undock

Bug: 26771328
Change-Id: I1fe12181cd80fd948e4184be6a8790c77bfe9e45
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index cd6dce0..5e33a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -34,4 +34,10 @@
     public static final Interpolator LINEAR = new LinearInterpolator();
     public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
     public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+
+    /**
+     * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+     */
+    public static final Interpolator TOUCH_RESPONSE =
+            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
new file mode 100644
index 0000000..d5083a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
@@ -0,0 +1,28 @@
+/*
+ * 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.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Fires when the user invoked the gesture to undock the task in the docked stack.
+ */
+public class UndockingTaskEvent extends EventBus.Event {
+
+    public UndockingTaskEvent() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 5ef56f3..12e2713 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -26,10 +26,9 @@
 import android.graphics.Paint;
 import android.util.AttributeSet;
 import android.util.Property;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageButton;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 /**
@@ -71,7 +70,6 @@
     private final int mWidth;
     private final int mHeight;
     private final int mCircleDiameter;
-    private final Interpolator mFastOutSlowInInterpolator;
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
@@ -85,8 +83,6 @@
         mCurrentWidth = mWidth;
         mCurrentHeight = mHeight;
         mCircleDiameter = (mWidth + mHeight) / 3;
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
     }
 
     public void setTouching(boolean touching, boolean animate) {
@@ -120,8 +116,8 @@
                 ? DividerView.TOUCH_ANIMATION_DURATION
                 : DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
         mAnimator.setInterpolator(touching
-                ? DividerView.TOUCH_RESPONSE_INTERPOLATOR
-                : mFastOutSlowInInterpolator);
+                ? Interpolators.TOUCH_RESPONSE
+                : Interpolators.FAST_OUT_SLOW_IN);
         mAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 40e2611..83c22b1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -48,10 +48,12 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
@@ -67,8 +69,6 @@
 
     static final long TOUCH_ANIMATION_DURATION = 150;
     static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
-    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
     private static final String TAG = "DividerView";
 
@@ -116,7 +116,6 @@
     private final Rect mOtherInsetRect = new Rect();
     private final Rect mLastResizeRect = new Rect();
     private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
-    private Interpolator mFastOutSlowInInterpolator;
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -158,8 +157,6 @@
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
         updateDisplayInfo();
@@ -229,8 +226,13 @@
 
     public void stopDragging(int position, SnapTarget target, long duration,
             Interpolator interpolator) {
+        stopDragging(position, target, duration, 0 /* startDelay*/, interpolator);
+    }
+
+    public void stopDragging(int position, SnapTarget target, long duration, long startDelay,
+            Interpolator interpolator) {
         mHandle.setTouching(false, true /* animate */);
-        flingTo(position, target, duration, interpolator);
+        flingTo(position, target, duration, startDelay, interpolator);
         mWindowManager.setSlippery(true);
         releaseBackground();
     }
@@ -335,10 +337,11 @@
         anim.start();
     }
 
-    private void flingTo(int position, SnapTarget target, long duration,
+    private void flingTo(int position, SnapTarget target, long duration, long startDelay,
             Interpolator interpolator) {
         ValueAnimator anim = getFlingAnimator(position, target);
         anim.setDuration(duration);
+        anim.setStartDelay(startDelay);
         anim.setInterpolator(interpolator);
         anim.start();
     }
@@ -392,7 +395,7 @@
             mBackground.animate().scaleX(1.4f);
         }
         mBackground.animate()
-                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setInterpolator(Interpolators.TOUCH_RESPONSE)
                 .setDuration(TOUCH_ANIMATION_DURATION)
                 .translationZ(mTouchElevation)
                 .start();
@@ -400,7 +403,7 @@
         // Lift handle as well so it doesn't get behind the background, even though it doesn't
         // cast shadow.
         mHandle.animate()
-                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setInterpolator(Interpolators.TOUCH_RESPONSE)
                 .setDuration(TOUCH_ANIMATION_DURATION)
                 .translationZ(mTouchElevation)
                 .start();
@@ -408,14 +411,14 @@
 
     private void releaseBackground() {
         mBackground.animate()
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .scaleX(1f)
                 .scaleY(1f)
                 .start();
         mHandle.animate()
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .start();
@@ -743,13 +746,27 @@
             mAnimateAfterRecentsDrawn = false;
             updateDockSide();
             stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    TOUCH_RESPONSE_INTERPOLATOR);
+                    Interpolators.TOUCH_RESPONSE);
         }
         if (mGrowAfterRecentsDrawn) {
             mGrowAfterRecentsDrawn = false;
             updateDockSide();
             stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    TOUCH_RESPONSE_INTERPOLATOR);
+                    Interpolators.TOUCH_RESPONSE);
+        }
+    }
+
+    public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) {
+        int dockSide = mWindowManagerProxy.getDockSide();
+        if (dockSide != WindowManager.DOCKED_INVALID) {
+            startDragging(false /* animate */, false /* touching */);
+            SnapTarget target = dockSideTopLeft(dockSide)
+                    ? mSnapAlgorithm.getDismissEndTarget()
+                    : mSnapAlgorithm.getDismissStartTarget();
+
+            // Don't start immediately - give a little bit time to settle the drag resize change.
+            stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */,
+                    Interpolators.TOUCH_RESPONSE);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 575eda7..f822bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,8 +39,6 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -212,10 +210,6 @@
         }
     };
 
-    /** Interpolator to be used for animations that respond directly to a touch */
-    private final Interpolator mTouchResponseInterpolator =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(!DEBUG);
@@ -1447,7 +1441,7 @@
         mScrollView.setBlockFlinging(true);
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
         if (isClick) {
-            animator.setInterpolator(mTouchResponseInterpolator);
+            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
             animator.setDuration(368);
         } else {
             mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 21c1295..4dee51d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -118,7 +118,10 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -1112,17 +1115,25 @@
         @Override
         public boolean onLongClick(View v) {
             if (mRecents != null) {
-                Point realSize = new Point();
-                mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
-                        .getRealSize(realSize);
-                Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
-                boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                        ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
-                        initialBounds);
-                if (docked) {
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
+                int dockSide = WindowManagerProxy.getInstance().getDockSide();
+                if (dockSide == WindowManager.DOCKED_INVALID) {
+                    Point realSize = new Point();
+                    mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+                            .getRealSize(realSize);
+                    Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
+                    boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+                            ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                            initialBounds);
+                    if (docked) {
+                        MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
+                        return true;
+                    }
+                } else {
+                    EventBus.getDefault().send(new UndockingTaskEvent());
+                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
                     return true;
                 }
+
             }
             return false;
         }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 0e67a24..85951ef 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -342,11 +342,15 @@
 
     // QS Tile for Data Saver.
     QS_DATA_SAVER = 284;
-
+    
     // OPEN: Settings > Security > User credentials
     // CATEGORY: Settings
     // OS: 6.1
     // GMS: 7.8.99
     USER_CREDENTIALS = 285;
+
+    // Logged when the user undocks a previously docked window by long pressing recents while in
+    // docked mode.
+    ACTION_WINDOW_UNDOCK_LONGPRESS = 285;
   }
 }