Dismissing of docked stack by dragging to the side.

Bug: 24623909
Change-Id: Ic93b169442de1eb4dca9f65a87ae2b813bcef043
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 42888cf..faa3a43 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2691,6 +2691,13 @@
             reply.writeNoException();
             return true;
         }
+        case REMOVE_STACK: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int stackId = data.readInt();
+            removeStack(stackId);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -6239,5 +6246,17 @@
         reply.recycle();
     }
 
+    @Override
+    public void removeStack(int stackId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(stackId);
+        mRemote.transact(REMOVE_STACK, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ae82bd6..e23620d 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -16,8 +16,8 @@
 
 package android.app;
 
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.RunningServiceInfo;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -538,6 +538,8 @@
 
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
 
+    public void removeStack(int stackId) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -895,4 +897,5 @@
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
     int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
+    int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dac317a..91eaf93 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17572,6 +17572,24 @@
     }
 
     @Override
+    public void removeStack(int stackId) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "detahStack()");
+        if (stackId == HOME_STACK_ID) {
+            throw new IllegalArgumentException("Removing home stack is not allowed.");
+        }
+        synchronized (this) {
+            ActivityStack stack = mStackSupervisor.getStack(stackId);
+            if (stack != null) {
+                ArrayList<TaskRecord> tasks = stack.getAllTasks();
+                for (int i = tasks.size() - 1; i >= 0; i--) {
+                    removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */);
+                }
+            }
+        }
+    }
+
+    @Override
     public void updatePersistentConfiguration(Configuration values) {
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 8c479d8..bc31274 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -29,6 +29,10 @@
     private static final String TAG = "DimLayer";
     private static final boolean DEBUG = false;
 
+    public static final float RESIZING_HINT_ALPHA = 0.5f;
+
+    public static final int RESIZING_HINT_DURATION_MS = 0;
+
     /** Actual surface that dims */
     SurfaceControl mDimSurface;
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b9028e3..120b077 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -25,16 +25,22 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
+import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP;
 import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
 import static com.android.server.wm.TaskStack.DOCKED_LEFT;
 import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
 import static com.android.server.wm.TaskStack.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.RemoteException;
+import android.util.Slog;
+import android.view.DisplayInfo;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -44,11 +50,13 @@
 /**
  * Controls showing and hiding of a docked stack divider on the display.
  */
-public class DockedStackDividerController implements View.OnTouchListener {
+public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser {
     private static final String TAG = "DockedStackDivider";
     private final Context mContext;
     private final int mDividerWidth;
     private final DisplayContent mDisplayContent;
+    private final int mSideMargin;
+    private final DimLayer mDimLayer;
     private View mView;
     private Rect mTmpRect = new Rect();
     private Rect mLastResizeRect = new Rect();
@@ -57,12 +65,15 @@
     private TaskStack mTaskStack;
     private Rect mOriginalRect = new Rect();
     private int mDockSide;
+    private boolean mDimLayerVisible;
 
     DockedStackDividerController(Context context, DisplayContent displayContent) {
         mContext = context;
         mDisplayContent = displayContent;
         mDividerWidth = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
+        mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
     }
 
     private void addDivider(Configuration configuration) {
@@ -154,11 +165,17 @@
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (mTaskStack != null) {
-                    resizeStack(event);
+                    final int x = (int) event.getRawX();
+                    final int y = (int) event.getRawY();
+                    resizeStack(x, y);
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+                if (mTaskStack != null) {
+                    maybeDismissTaskStack((int) event.getRawX(), (int) event.getRawY());
+                }
+                setDimLayerVisible(false, -1, -1);
                 mTaskStack = null;
                 mDockSide = TaskStack.DOCKED_INVALID;
                 break;
@@ -166,10 +183,88 @@
         return true;
     }
 
-    private void resizeStack(MotionEvent event) {
+    private void maybeDismissTaskStack(int x, int y) {
+        final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+        if (distance == -1) {
+            Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide);
+            return;
+        }
+        if (distance <= mSideMargin) {
+            try {
+                mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId);
+            } catch (RemoteException e) {
+                // This can't happen because we are in the same process.
+            }
+        }
+    }
+
+    private void updateDimLayer(int x, int y) {
+        final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+        if (distance == -1) {
+            Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
+            return;
+        }
+        setDimLayerVisible(distance <= mSideMargin, x, y);
+    }
+
+    /**
+     * Provides the distance from the point to the docked side of a rectangle.
+     *
+     * @return non negative distance or -1 on error
+     */
+    private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) {
+        switch (dockSide) {
+            case DOCKED_LEFT:
+                return x - bounds.left;
+            case DOCKED_TOP:
+                return y - bounds.top;
+            case DOCKED_RIGHT:
+                return bounds.right - x;
+            case DOCKED_BOTTOM:
+                return bounds.bottom - y;
+        }
+        return -1;
+    }
+
+    private void setDimLayerVisible(boolean visible, int x, int y) {
+        if (visible) {
+            mTmpRect.set(mOriginalRect);
+            switch (mDockSide) {
+                case DOCKED_LEFT:
+                    mTmpRect.right = x;
+                    break;
+                case DOCKED_TOP:
+                    mTmpRect.bottom = y;
+                    break;
+                case DOCKED_RIGHT:
+                    mTmpRect.left = x;
+                    break;
+                case DOCKED_BOTTOM:
+                    mTmpRect.top = y;
+                    break;
+                default:
+                    Slog.wtf(TAG, "setDimLayerVisible: Unknown dock side when setting dim layer="
+                            + mDockSide);
+                    return;
+            }
+            mDimLayer.setBounds(mTmpRect);
+        }
+        if (mDimLayerVisible == visible) {
+            return;
+        }
+        mDimLayerVisible = visible;
+        if (mDimLayerVisible) {
+            mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+                    RESIZING_HINT_DURATION_MS);
+        } else {
+            mDimLayer.hide();
+        }
+    }
+
+    private void resizeStack(int x, int y) {
         mTmpRect.set(mOriginalRect);
-        final int deltaX = (int) event.getRawX() - mStartX;
-        final int deltaY = (int) event.getRawY() - mStartY;
+        final int deltaX = x - mStartX;
+        final int deltaY = y - mStartY;
         switch (mDockSide) {
             case DOCKED_LEFT:
                 mTmpRect.right += deltaX;
@@ -191,7 +286,9 @@
         try {
             mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect);
         } catch (RemoteException e) {
+            // This can't happen because we are in the same process.
         }
+        updateDimLayer(x, y);
     }
 
     boolean isResizing() {
@@ -201,4 +298,24 @@
     int getWidthAdjustment() {
         return getWidth() / 2;
     }
+
+    @Override
+    public boolean isFullscreen() {
+        return false;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    @Override
+    public void getBounds(Rect outBounds) {
+        // This dim layer user doesn't need this.
+    }
+
+    @Override
+    public String toShortString() {
+        return TAG;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 207da47..227b3f0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -23,8 +23,11 @@
 import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
@@ -60,7 +63,7 @@
 
     // The margin the pointer position has to be within the side of the screen to be
     // considered at the side of the screen.
-    private static final int SIDE_MARGIN_DIP = 100;
+    static final int SIDE_MARGIN_DIP = 100;
 
     @IntDef(flag = true,
             value = {
@@ -246,7 +249,7 @@
                 mDisplay.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.inputChannel = mServerChannel;
-        mDragWindowHandle.layer = getDragLayerLocked();
+        mDragWindowHandle.layer = mService.getDragLayerLocked();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
         mDragWindowHandle.dispatchingTimeoutNanos =
@@ -279,9 +282,9 @@
         mService.pauseRotationLocked();
 
         mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
-        mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
-        mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
-        mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+        mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
+        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
 
         mDragEnded = false;
     }
@@ -455,7 +458,8 @@
         }
 
         mDimLayer.setBounds(mTmpRect);
-        mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+        mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+                RESIZING_HINT_DURATION_MS);
     }
 
     @Override /** {@link DimLayer.DimLayerUser} */
@@ -477,10 +481,4 @@
     public String toShortString() {
         return TAG;
     }
-
-    private int getDragLayerLocked() {
-        return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
-    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index a630993..ca358b1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.*;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.H.UNUSED;
 import static com.android.server.wm.WindowManagerService.TAG;
 
 import android.annotation.IntDef;
@@ -177,9 +178,9 @@
 
         if (mDisplayContent != null) {
             mDisplayContent.mDimBehindController.updateDimLayer(this);
+            mAnimationBackgroundSurface.setBounds(bounds);
         }
 
-        mAnimationBackgroundSurface.setBounds(bounds);
         mBounds.set(bounds);
         mRotation = rotation;
         return true;
@@ -238,7 +239,7 @@
                     // a one-way call. We do this to prevent a deadlock between window manager
                     // lock and activity manager lock been held.
                     mService.mH.sendMessage(
-                            mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+                            mService.mH.obtainMessage(RESIZE_STACK, mStackId, UNUSED, mBounds));
                 }
             }
         }
@@ -488,7 +489,7 @@
                     && otherStackId >= FIRST_STATIC_STACK_ID
                     && otherStackId <= LAST_STATIC_STACK_ID) {
                 mService.mH.sendMessage(
-                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId, -1, bounds));
+                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId, UNUSED, bounds));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3cf8500..6defcf5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -465,6 +465,11 @@
 
     static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 
+    int getDragLayerLocked() {
+        return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER
+                + TYPE_LAYER_OFFSET;
+    }
+
     class RotationWatcher {
         IRotationWatcher watcher;
         IBinder.DeathRecipient deathRecipient;