diff options
13 files changed, 553 insertions, 141 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index d2d7b2c0bdb1..51ce7af1786c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -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(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5ab8694ef7ca..e7f430f96129 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -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; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 13642ebddb17..a1d3a2aba14f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index ef47d8d1f2d1..933d05ce2b89 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 79bd626efdb6..c50db3b55c51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -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()); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 755790650dd5..5a9969e09a98 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9559,7 +9559,26 @@ public final class ActivityManagerService extends ActivityManagerNative try { synchronized (this) { mStackSupervisor.resizeStackLocked( - stackId, bounds, !PRESERVE_WINDOWS, allowResizeInDockedMode); + stackId, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, + !PRESERVE_WINDOWS, allowResizeInDockedMode); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { + enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, + "resizeDockedStack()"); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, + tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds, + PRESERVE_WINDOWS); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4f8f6707c178..77580297b056 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -379,6 +379,7 @@ public final class ActivityStackSupervisor implements DisplayListener { private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>(); private final SparseArray<Rect> mTmpBounds = new SparseArray<>(); + private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>(); // The default minimal size that will be used if the activity doesn't specify its minimal size. // It will be calculated when the default display gets added. @@ -389,6 +390,8 @@ public final class ActivityStackSupervisor implements DisplayListener { private final ActivityMetricsLogger mActivityMetricsLogger; + private final ResizeDockedStackTimeout mResizeDockedStackTimeout; + static class FindTaskResult { ActivityRecord r; boolean matchedByRootAffinity; @@ -432,6 +435,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mRecentTasks = recentTasks; mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); + mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler); } /** @@ -1799,16 +1803,20 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void resizeStackLocked(int stackId, Rect bounds, boolean preserveWindows, - boolean allowResizeInDockedMode) { + void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds, + boolean preserveWindows, boolean allowResizeInDockedMode) { + if (stackId == DOCKED_STACK_ID) { + resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null, + preserveWindows); + return; + } final ActivityStack stack = getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; } - if (!allowResizeInDockedMode - && stackId != DOCKED_STACK_ID && getStack(DOCKED_STACK_ID) != null) { + if (!allowResizeInDockedMode && getStack(DOCKED_STACK_ID) != null) { // If the docked stack exist we don't allow resizes of stacks not caused by the docked // stack size changing so things don't get out of sync. return; @@ -1817,94 +1825,134 @@ public final class ActivityStackSupervisor implements DisplayListener { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId); mWindowManager.deferSurfaceLayout(); try { + resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds); + ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows); + } finally { + mWindowManager.continueSurfaceLayout(); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + } - if (bounds != null && mWindowManager.isFullscreenBounds(stackId, bounds)) { - // The bounds passed in corresponds to the fullscreen bounds which we normally - // represent with null. Go ahead and set it to null so that all tasks configuration - // can have the right fullscreen state. - bounds = null; - } - - ActivityRecord r = stack.topRunningActivityLocked(); + private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds, + Rect tempTaskInsetBounds) { + if (bounds != null && mWindowManager.isFullscreenBounds(stack.mStackId, bounds)) { + // The bounds passed in corresponds to the fullscreen bounds which we normally + // represent with null. Go ahead and set it to null so that all tasks configuration + // can have the right fullscreen state. + bounds = null; + } - mTmpBounds.clear(); - mTmpConfigs.clear(); - ArrayList<TaskRecord> tasks = stack.getAllTasks(); - for (int i = tasks.size() - 1; i >= 0; i--) { - TaskRecord task = tasks.get(i); - if (task.mResizeable) { - if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { - // For freeform stack we don't adjust the size of the tasks to match that - // of the stack, but we do try to make sure the tasks are still contained - // with the bounds of the stack. - tempRect2.set(task.mBounds); - fitWithinBounds(tempRect2, bounds); - task.updateOverrideConfiguration(tempRect2); - } else { - task.updateOverrideConfiguration(bounds); - } + mTmpBounds.clear(); + mTmpConfigs.clear(); + mTmpInsetBounds.clear(); + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + TaskRecord task = tasks.get(i); + if (task.mResizeable) { + if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { + // For freeform stack we don't adjust the size of the tasks to match that + // of the stack, but we do try to make sure the tasks are still contained + // with the bounds of the stack. + tempRect2.set(task.mBounds); + fitWithinBounds(tempRect2, bounds); + task.updateOverrideConfiguration(tempRect2); + } else { + task.updateOverrideConfiguration(tempTaskBounds != null + ? tempTaskBounds : bounds); } + } - mTmpConfigs.put(task.taskId, task.mOverrideConfig); - mTmpBounds.put(task.taskId, task.mBounds); + mTmpConfigs.put(task.taskId, task.mOverrideConfig); + mTmpBounds.put(task.taskId, task.mBounds); + if (tempTaskInsetBounds != null) { + mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds); } - stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds); - if (stack.mStackId == DOCKED_STACK_ID) { - // Dock stack funness...Yay! - if (stack.mFullscreen) { - // The dock stack went fullscreen which is kinda like dismissing it. - // In this case we make all other static stacks fullscreen and move all - // docked stack tasks to the fullscreen stack. - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) { - resizeStackLocked(i, null, preserveWindows, true); - } - } + } + stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs, + mTmpBounds, mTmpInsetBounds); + stack.setBounds(bounds); + } - final int count = tasks.size(); - for (int i = 0; i < count; i++) { - moveTaskToStackLocked(tasks.get(i).taskId, - FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack", - false /* animate */); - } + private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r, + boolean preserveWindows) { + if (r == null) { + return; + } + final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, + preserveWindows); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + ensureActivitiesVisibleLocked(r, 0, preserveWindows); + if (!updated) { + resumeFocusedStackTopActivityLocked(); + } + } - // stack shouldn't contain anymore activities, so nothing to resume. - r = null; - } else { - // Docked stacks occupy a dedicated region on screen so the size of all other - // static stacks need to be adjusted so they don't overlap with the docked stack. - // We get the bounds to use from window manager which has been adjusted for any - // screen controls and is also the same for all stacks. - mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect); - - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - if (StackId.isResizeableByDockedStack(i)) { - ActivityStack otherStack = getStack(i); - if (otherStack != null) { - resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true); - } - } + void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) { + final ActivityStack stack = getStack(DOCKED_STACK_ID); + if (stack == null) { + Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); + return; + } + + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack"); + mWindowManager.deferSurfaceLayout(); + try { + ActivityRecord r = stack.topRunningActivityLocked(); + resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds, + tempDockedTaskInsetBounds); + + if (stack.mFullscreen) { + // The dock stack went fullscreen which is kinda like dismissing it. + // In this case we make all other static stacks fullscreen and move all + // docked stack tasks to the fullscreen stack. + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) { + resizeStackLocked(i, null, null, null, preserveWindows, + true /* allowResizeInDockedMode */); } } - // Since we are resizing the stack, all other operations should strive to preserve - // windows. - preserveWindows = true; - } - stack.setBounds(bounds); - if (r != null) { - final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows); - // And we need to make sure at this point that all other activities - // are made visible with the correct configuration. - ensureActivitiesVisibleLocked(r, 0, preserveWindows); - if (!updated) { - resumeFocusedStackTopActivityLocked(); + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + final int count = tasks.size(); + for (int i = 0; i < count; i++) { + moveTaskToStackLocked(tasks.get(i).taskId, + FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack", + false /* animate */); + } + + // stack shouldn't contain anymore activities, so nothing to resume. + r = null; + } else { + // Docked stacks occupy a dedicated region on screen so the size of all other + // static stacks need to be adjusted so they don't overlap with the docked stack. + // We get the bounds to use from window manager which has been adjusted for any + // screen controls and is also the same for all stacks. + mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect); + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (StackId.isResizeableByDockedStack(i)) { + ActivityStack otherStack = getStack(i); + if (otherStack != null) { + resizeStackLocked(i, tempRect, tempOtherTaskBounds, + tempOtherTaskInsetBounds, preserveWindows, + true /* allowResizeInDockedMode */); + } + } } } + ensureConfigurationAndResume(stack, r, preserveWindows); } finally { mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } + + mResizeDockedStackTimeout.notifyResizing(dockedBounds, + tempDockedTaskBounds != null + || tempDockedTaskInsetBounds != null + || tempOtherTaskBounds != null + || tempOtherTaskInsetBounds != null); } void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { @@ -2193,7 +2241,8 @@ public final class ActivityStackSupervisor implements DisplayListener { } if (bounds != null) { - resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS, true); + resizeStackLocked(stackId, bounds, null /* tempTaskBounds */, + null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true); } // The task might have already been running and its visibility needs to be synchronized with diff --git a/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java new file mode 100644 index 000000000000..ff395895e638 --- /dev/null +++ b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java @@ -0,0 +1,63 @@ +/* + * 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.server.am; + +import android.graphics.Rect; +import android.os.Handler; + +import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; + +/** + * When resizing the docked stack, a caller can temporarily supply task bounds that are different + * from the stack bounds. In order to return to a sane state if the caller crashes or has a bug, + * this class manages this cycle. + */ +class ResizeDockedStackTimeout { + + private static final long TIMEOUT_MS = 10 * 1000; + private final ActivityManagerService mService; + private final ActivityStackSupervisor mSupervisor; + private final Handler mHandler; + private final Rect mCurrentDockedBounds = new Rect(); + + private final Runnable mTimeoutRunnable = new Runnable() { + @Override + public void run() { + synchronized (mService) { + mSupervisor.resizeDockedStackLocked(mCurrentDockedBounds, null, null, null, null, + PRESERVE_WINDOWS); + } + } + }; + + ResizeDockedStackTimeout(ActivityManagerService service, ActivityStackSupervisor supervisor, + Handler handler) { + mService = service; + mSupervisor = supervisor; + mHandler = handler; + } + + void notifyResizing(Rect dockedBounds, boolean hasTempBounds) { + mHandler.removeCallbacks(mTimeoutRunnable); + if (!hasTempBounds) { + return; + } + mCurrentDockedBounds.set(dockedBounds); + mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS); + } + +} diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 27e7a31bd07b..6e65ac19589c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -62,6 +62,9 @@ class Task implements DimLayer.DimLayerUser { // Content limits relative to the DisplayContent this sits in. private Rect mBounds = new Rect(); + // Bounds used to calculate the insets. + private final Rect mTempInsetBounds = new Rect(); + // Device rotation as of the last time {@link #mBounds} was set. int mRotation; @@ -267,6 +270,26 @@ class Task implements DimLayer.DimLayerUser { return boundsChange; } + /** + * Sets the bounds used to calculate the insets. See + * {@link android.app.IActivityManager#resizeDockedStack} why this is needed. + */ + void setTempInsetBounds(Rect tempInsetBounds) { + if (tempInsetBounds != null) { + mTempInsetBounds.set(tempInsetBounds); + } else { + mTempInsetBounds.setEmpty(); + } + } + + /** + * Gets the bounds used to calculate the insets. See + * {@link android.app.IActivityManager#resizeDockedStack} why this is needed. + */ + void getTempInsetBounds(Rect out) { + out.set(mTempInsetBounds); + } + void setResizeable(boolean resizeable) { mResizeable = resizeable; } @@ -357,7 +380,6 @@ class Task implements DimLayer.DimLayerUser { mStack.getDisplayContent().getLogicalDisplayRect(out); } - /** * Calculate the maximum visible area of this task. If the task has only one app, * the result will be visible frame of that app. If the task has more than one apps, diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index b9618791b848..e481d116a69f 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -114,7 +114,8 @@ public class TaskStack implements DimLayer.DimLayerUser { * @return True if the stack bounds was changed. * */ boolean setBounds( - Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) { + Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { if (!setBounds(stackBounds)) { return false; } @@ -136,6 +137,9 @@ public class TaskStack implements DimLayer.DimLayerUser { task.scrollLocked(mTmpRect); } else { task.setBounds(bounds, config); + task.setTempInsetBounds( + taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) + : null); } } else { Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 06e2e30d6050..e4299d94b749 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4824,14 +4824,16 @@ public class WindowManagerService extends IWindowManager.Stub * @return True if the stack is now fullscreen. * */ public boolean resizeStack(int stackId, Rect bounds, - SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) { + SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { synchronized (mWindowMap) { final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { throw new IllegalArgumentException("resizeStack: stackId " + stackId + " not found."); } - if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) { + if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds) + && stack.isVisibleLocked()) { stack.resizeWindows(); stack.getDisplayContent().layoutNeeded = true; mWindowPlacerLocked.performSurfacePlacement(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a825e80e26b2..0eb1c6e7d9d9 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -309,6 +309,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { // possible to draw. final Rect mOutsetFrame = new Rect(); + /** + * Usually empty. Set to the task's tempInsetFrame. See + *{@link android.app.IActivityManager#resizeDockedStack}. + */ + final Rect mInsetFrame = new Rect(); + boolean mContentChanged; // If a window showing a wallpaper: the requested offset for the @@ -614,8 +620,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { // We use the parent frame as the containing frame for fullscreen and child windows mContainingFrame.set(pf); mDisplayFrame.set(df); + mInsetFrame.setEmpty(); } else { task.getBounds(mContainingFrame); + task.getTempInsetBounds(mInsetFrame); final WindowState imeWin = mService.mInputMethodWindow; if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this && mContainingFrame.bottom > cf.bottom) { @@ -674,6 +682,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { mOutsets.set(0, 0, 0, 0); } + // Denotes the actual frame used to calculate the insets. When resizing in docked mode, + // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the + // notion of a task having a different inset frame, we can achieve that while still moving + // the task around. + final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame; + // Make sure the content and visible frames are inside of the // final window frame. if (freeformWorkspace && !mFrame.isEmpty()) { @@ -709,42 +723,59 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContentFrame.set(mFrame); } } else { - mContentFrame.set(Math.max(mContentFrame.left, mFrame.left), - Math.max(mContentFrame.top, mFrame.top), - Math.min(mContentFrame.right, mFrame.right), - Math.min(mContentFrame.bottom, mFrame.bottom)); - - mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left), - Math.max(mVisibleFrame.top, mFrame.top), - Math.min(mVisibleFrame.right, mFrame.right), - Math.min(mVisibleFrame.bottom, mFrame.bottom)); - - mStableFrame.set(Math.max(mStableFrame.left, mFrame.left), - Math.max(mStableFrame.top, mFrame.top), - Math.min(mStableFrame.right, mFrame.right), - Math.min(mStableFrame.bottom, mFrame.bottom)); - } - - mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0), - Math.max(mOverscanFrame.top - mFrame.top, 0), - Math.max(mFrame.right - mOverscanFrame.right, 0), - Math.max(mFrame.bottom - mOverscanFrame.bottom, 0)); - - mContentInsets.set(mContentFrame.left - mFrame.left, - mContentFrame.top - mFrame.top, - mFrame.right - mContentFrame.right, - mFrame.bottom - mContentFrame.bottom); - - mVisibleInsets.set(mVisibleFrame.left - mFrame.left, - mVisibleFrame.top - mFrame.top, - mFrame.right - mVisibleFrame.right, - mFrame.bottom - mVisibleFrame.bottom); - - mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0), - Math.max(mStableFrame.top - mFrame.top, 0), - Math.max(mFrame.right - mStableFrame.right, 0), - Math.max(mFrame.bottom - mStableFrame.bottom, 0)); - + mContentFrame.set(Math.max(mContentFrame.left, frame.left), + Math.max(mContentFrame.top, frame.top), + Math.min(mContentFrame.right, frame.right), + Math.min(mContentFrame.bottom, frame.bottom)); + + mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left), + Math.max(mVisibleFrame.top, frame.top), + Math.min(mVisibleFrame.right, frame.right), + Math.min(mVisibleFrame.bottom, frame.bottom)); + + mStableFrame.set(Math.max(mStableFrame.left, frame.left), + Math.max(mStableFrame.top, frame.top), + Math.min(mStableFrame.right, frame.right), + Math.min(mStableFrame.bottom, frame.bottom)); + } + + mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), + Math.max(mOverscanFrame.top - frame.top, 0), + Math.max(frame.right - mOverscanFrame.right, 0), + Math.max(frame.bottom - mOverscanFrame.bottom, 0)); + + mContentInsets.set(mContentFrame.left - frame.left, + mContentFrame.top - frame.top, + frame.right - mContentFrame.right, + frame.bottom - mContentFrame.bottom); + + mVisibleInsets.set(mVisibleFrame.left - frame.left, + mVisibleFrame.top - frame.top, + frame.right - mVisibleFrame.right, + frame.bottom - mVisibleFrame.bottom); + + mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0), + Math.max(mStableFrame.top - frame.top, 0), + Math.max(frame.right - mStableFrame.right, 0), + Math.max(frame.bottom - mStableFrame.bottom, 0)); + + if (!mInsetFrame.isEmpty()) { + mContentFrame.set(mFrame); + mContentFrame.top += mContentInsets.top; + mContentFrame.bottom += mContentInsets.bottom; + mContentFrame.left += mContentInsets.left; + mContentFrame.right += mContentInsets.right; + mVisibleFrame.set(mFrame); + mVisibleFrame.top += mVisibleInsets.top; + mVisibleFrame.bottom += mVisibleInsets.bottom; + mVisibleFrame.left += mVisibleInsets.left; + mVisibleFrame.right += mVisibleInsets.right; + mStableFrame.set(mFrame); + mStableFrame.top += mStableInsets.top; + mStableFrame.bottom += mStableInsets.bottom; + mStableFrame.left += mStableInsets.left; + mStableFrame.right += mStableInsets.right; + } mCompatFrame.set(mFrame); if (mEnforceSizeCompat) { // If there is a size compatibility scale being applied to the @@ -1950,8 +1981,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { // isDragResizing() or isDragResizeChanged() is true. boolean resizing = isDragResizing() || isDragResizeChanged(); final Rect backDropFrame = (inFreeformWorkspace() || !resizing) ? frame : mTmpRect; - mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, - outsets, reportDraw, newConfig, backDropFrame); + mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, + reportDraw, newConfig, backDropFrame); } public void registerFocusObserver(IWindowFocusObserver observer) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 381db5609048..83ab19094fb0 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -37,6 +37,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.localLOGV; +import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; @@ -1063,7 +1064,16 @@ class WindowStateAnimator { final int top = w.mYOffset + w.mFrame.top; // Initialize the decor rect to the entire frame. - mSystemDecorRect.set(0, 0, width, height); + if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) { + + // If we are resizing with the divider, the task bounds might be smaller than the + // stack bounds. The system decor is used to clip to the task bounds, which we don't + // want in this case in order to avoid holes. + final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); + mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } else { + mSystemDecorRect.set(0, 0, width, height); + } // If a freeform window is animating from a position where it would be cutoff, it would be // cutoff during the animation. We don't want that, so for the duration of the animation |