Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc249c4a authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Change behavior when resizing docked stack

- Add an API resizeDockedStack to resize the docked stack
and supply temporary task bounds, which can be different from
the stack bounds.
- Use that API in SystemUI to only switch task bounds when
crossing thresholds, so we have less flickering and more
predictable resizing.

Bug: 25015474
Bug: 26311778
Change-Id: Id5c9277dd908ccc28f95dab023efc914757a50d0
parent c831e36b
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -785,6 +785,39 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
        }

        case RESIZE_DOCKED_STACK_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            final boolean hasBounds = data.readInt() != 0;
            Rect bounds = null;
            if (hasBounds) {
                bounds = Rect.CREATOR.createFromParcel(data);
            }
            final boolean hasTempDockedTaskBounds = data.readInt() != 0;
            Rect tempDockedTaskBounds = null;
            if (hasTempDockedTaskBounds) {
                tempDockedTaskBounds = Rect.CREATOR.createFromParcel(data);
            }
            final boolean hasTempDockedTaskInsetBounds = data.readInt() != 0;
            Rect tempDockedTaskInsetBounds = null;
            if (hasTempDockedTaskInsetBounds) {
                tempDockedTaskInsetBounds = Rect.CREATOR.createFromParcel(data);
            }
            final boolean hasTempOtherTaskBounds = data.readInt() != 0;
            Rect tempOtherTaskBounds = null;
            if (hasTempOtherTaskBounds) {
                tempOtherTaskBounds = Rect.CREATOR.createFromParcel(data);
            }
            final boolean hasTempOtherTaskInsetBounds = data.readInt() != 0;
            Rect tempOtherTaskInsetBounds = null;
            if (hasTempOtherTaskInsetBounds) {
                tempOtherTaskInsetBounds = Rect.CREATOR.createFromParcel(data);
            }
            resizeDockedStack(bounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
                    tempOtherTaskBounds, tempOtherTaskInsetBounds);
            reply.writeNoException();
            return true;
        }

        case POSITION_TASK_IN_STACK_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            int taskId = data.readInt();
@@ -3690,6 +3723,50 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
    }
    @Override
    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
            Rect tempDockedTaskInsetBounds,
            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds)
            throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        if (dockedBounds != null) {
            data.writeInt(1);
            dockedBounds.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        if (tempDockedTaskBounds != null) {
            data.writeInt(1);
            tempDockedTaskBounds.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        if (tempDockedTaskInsetBounds != null) {
            data.writeInt(1);
            tempDockedTaskInsetBounds.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        if (tempOtherTaskBounds != null) {
            data.writeInt(1);
            tempOtherTaskBounds.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        if (tempOtherTaskInsetBounds != null) {
            data.writeInt(1);
            tempOtherTaskInsetBounds.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(RESIZE_DOCKED_STACK_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }
    @Override
    public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException
    {
        Parcel data = Parcel.obtain();
+26 −1
Original line number Diff line number Diff line
@@ -145,7 +145,31 @@ public interface IActivityManager extends IInterface {
    public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
            Rect initialBounds) throws RemoteException;
    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode)
            throws RemoteException;

    /**
     * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
     *
     * @param dockedBounds The bounds for the docked stack.
     * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which
     *                             might be different from the stack bounds to allow more
     *                             flexibility while resizing, or {@code null} if they should be the
     *                             same as the stack bounds.
     * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets.
     *                                  When resizing, we usually "freeze" the layout of a task. To
     *                                  achieve that, we also need to "freeze" the insets, which
     *                                  gets achieved by changing task bounds but not bounds used
     *                                  to calculate the insets in this transient state
     * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or
     *                            {@code null} if they should be the same as the stack bounds.
     * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other
     *                                 stacks.
     * @throws RemoteException
     */
    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
            Rect tempDockedTaskInsetBounds,
            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) throws RemoteException;
    public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
    public List<StackInfo> getAllStackInfos() throws RemoteException;
    public StackInfo getStackInfo(int stackId) throws RemoteException;
@@ -922,4 +946,5 @@ public interface IActivityManager extends IInterface {
    int ENTER_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 355;
    int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356;
    int GET_URI_PERMISSION_OWNER_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 357;
    int RESIZE_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 358;
}
+78 −10
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,

    private static final String TAG = "DividerView";

    private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;

    private ImageButton mHandle;
    private View mBackground;
    private int mStartX;
@@ -73,7 +75,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    private int mDividerSize;
    private int mTouchElevation;

    private final Rect mTmpRect = new Rect();
    private final Rect mDockedRect = new Rect();
    private final Rect mDockedTaskRect = new Rect();
    private final Rect mOtherTaskRect = new Rect();
    private final Rect mOtherRect = new Rect();
    private final Rect mDockedInsetRect = new Rect();
    private final Rect mOtherInsetRect = new Rect();
    private final Rect mLastResizeRect = new Rect();
    private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
    private Interpolator mFastOutSlowInInterpolator;
@@ -82,6 +89,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    private DividerWindowManager mWindowManager;
    private VelocityTracker mVelocityTracker;
    private FlingAnimationUtils mFlingAnimationUtils;
    private DividerSnapAlgorithm mSnapAlgorithm;

    public DividerView(Context context) {
        super(context);
@@ -134,6 +142,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,

    public boolean startDragging() {
        mDockSide = mWindowManagerProxy.getDockSide();
        mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
                mDividerSize, isHorizontalDivision());
        if (mDockSide != WindowManager.DOCKED_INVALID) {
            mWindowManagerProxy.setResizing(true);
            mWindowManager.setSlippery(false);
@@ -150,6 +160,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
        releaseBackground();
    }

    public DividerSnapAlgorithm getSnapAlgorithm() {
        return mSnapAlgorithm;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        convertToScreenCoordinates(event);
@@ -173,7 +187,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                int x = (int) event.getX();
                int y = (int) event.getY();
                if (mDockSide != WindowManager.DOCKED_INVALID) {
                    resizeStack(calculatePosition(x, y));
                    int position = calculatePosition(x, y);
                    SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
                            0 /* velocity */);
                    resizeStack(calculatePosition(x, y), snapTarget.position);
                }
                break;
            case MotionEvent.ACTION_UP:
@@ -197,14 +214,16 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    }

    private void fling(int position, float velocity) {
        final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
                mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity);
        final SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);

        ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                resizeStack((Integer) animation.getAnimatedValue());
                resizeStack((Integer) animation.getAnimatedValue(),
                        animation.getAnimatedFraction() == 1f
                                ? TASK_POSITION_SAME
                                : snapTarget.position);
            }
        });
        anim.addListener(new AnimatorListenerAdapter() {
@@ -332,17 +351,66 @@ public class DividerView extends FrameLayout implements OnTouchListener,
        }
    }

    public void resizeStack(int position) {
        calculateBoundsForPosition(position, mDockSide, mTmpRect);
        if (mTmpRect.equals(mLastResizeRect)) {
    private int invertDockSide(int dockSide) {
        switch (dockSide) {
            case WindowManager.DOCKED_LEFT:
                return WindowManager.DOCKED_RIGHT;
            case WindowManager.DOCKED_TOP:
                return WindowManager.DOCKED_BOTTOM;
            case WindowManager.DOCKED_RIGHT:
                return WindowManager.DOCKED_LEFT;
            case WindowManager.DOCKED_BOTTOM:
                return WindowManager.DOCKED_TOP;
            default:
                return WindowManager.DOCKED_INVALID;
        }
    }

    private void alignTopLeft(Rect containingRect, Rect rect) {
        int width = rect.width();
        int height = rect.height();
        rect.set(containingRect.left, containingRect.top,
                containingRect.left + width, containingRect.top + height);
    }

    private void alignBottomRight(Rect containingRect, Rect rect) {
        int width = rect.width();
        int height = rect.height();
        rect.set(containingRect.right - width, containingRect.bottom - height,
                containingRect.right, containingRect.bottom);
    }

    public void resizeStack(int position, int taskPosition) {
        calculateBoundsForPosition(position, mDockSide, mDockedRect);

        if (mDockedRect.equals(mLastResizeRect)) {
            return;
        }

        // Make sure shadows are updated
        mBackground.invalidate();

        mLastResizeRect.set(mTmpRect);
        mWindowManagerProxy.resizeDockedStack(mTmpRect);
        mLastResizeRect.set(mDockedRect);
        if (taskPosition != TASK_POSITION_SAME) {
            calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect);
            calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
            calculateBoundsForPosition(taskPosition, invertDockSide(mDockSide), mOtherTaskRect);
            alignTopLeft(mDockedRect, mDockedTaskRect);
            alignTopLeft(mOtherRect, mOtherTaskRect);
            mDockedInsetRect.set(mDockedTaskRect);
            mOtherInsetRect.set(mOtherTaskRect);
            if (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP) {
                alignTopLeft(mDockedRect, mDockedInsetRect);
                alignBottomRight(mOtherRect, mOtherInsetRect);
            } else {
                alignBottomRight(mDockedRect, mDockedInsetRect);
                alignTopLeft(mOtherRect, mOtherInsetRect);
            }
            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
                    mOtherTaskRect, mOtherInsetRect);
        } else {
            mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
        }
    }

    @Override
+47 −8
Original line number Diff line number Diff line
@@ -40,18 +40,36 @@ public class WindowManagerProxy {
    private static final WindowManagerProxy sInstance = new WindowManagerProxy();

    @GuardedBy("mResizeRect")
    private final Rect mResizeRect = new Rect();
    private final Rect mTmpRect = new Rect();
    private final Rect mDockedRect = new Rect();
    private final Rect mTempDockedTaskRect = new Rect();
    private final Rect mTempDockedInsetRect = new Rect();
    private final Rect mTempOtherTaskRect = new Rect();
    private final Rect mTempOtherInsetRect = new Rect();

    private final Rect mTmpRect1 = new Rect();
    private final Rect mTmpRect2 = new Rect();
    private final Rect mTmpRect3 = new Rect();
    private final Rect mTmpRect4 = new Rect();
    private final Rect mTmpRect5 = new Rect();
    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    private final Runnable mResizeRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mResizeRect) {
                mTmpRect.set(mResizeRect);
            synchronized (mDockedRect) {
                mTmpRect1.set(mDockedRect);
                mTmpRect2.set(mTempDockedTaskRect);
                mTmpRect3.set(mTempDockedInsetRect);
                mTmpRect4.set(mTempOtherTaskRect);
                mTmpRect5.set(mTempOtherInsetRect);
            }
            try {
                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, mTmpRect, true);
                ActivityManagerNative.getDefault()
                        .resizeDockedStack(mTmpRect1,
                                mTmpRect2.isEmpty() ? null : mTmpRect2,
                                mTmpRect3.isEmpty() ? null : mTmpRect3,
                                mTmpRect4.isEmpty() ? null : mTmpRect4,
                                mTmpRect5.isEmpty() ? null : mTmpRect5);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to resize stack: " + e);
            }
@@ -87,9 +105,30 @@ public class WindowManagerProxy {
        return sInstance;
    }

    public void resizeDockedStack(Rect rect) {
        synchronized (mResizeRect) {
            mResizeRect.set(rect);
    public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
            Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
        synchronized (mDockedRect) {
            mDockedRect.set(docked);
            if (tempDockedTaskRect != null) {
                mTempDockedTaskRect.set(tempDockedTaskRect);
            } else {
                mTempDockedTaskRect.setEmpty();
            }
            if (tempDockedInsetRect != null) {
                mTempDockedInsetRect.set(tempDockedInsetRect);
            } else {
                mTempDockedInsetRect.setEmpty();
            }
            if (tempOtherTaskRect != null) {
                mTempOtherTaskRect.set(tempOtherTaskRect);
            } else {
                mTempOtherTaskRect.setEmpty();
            }
            if (tempOtherInsetRect != null) {
                mTempOtherInsetRect.set(tempOtherInsetRect);
            } else {
                mTempOtherInsetRect.setEmpty();
            }
        }
        mExecutor.execute(mResizeRunnable);
    }
+4 −1
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.DividerView;
import com.android.systemui.tuner.TunerService;

import static android.view.WindowManager.*;
@@ -198,8 +199,10 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
            }
        } else {
            if (mDragMode == DRAG_MODE_DIVIDER) {
                int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
                mDivider.getView().resizeStack(
                        !mIsVertical ? (int) event.getRawY() : (int) event.getRawX());
                        position, mDivider.getView().getSnapAlgorithm()
                                .calculateSnapTarget(position, 0f /* velocity */).position);
            } else if (mDragMode == DRAG_MODE_RECENTS) {
                mRecentsComponent.onDraggingInRecents(event.getRawY());
            }
Loading