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();
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();
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;
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;
}
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;
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;
private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
+ private DividerSnapAlgorithm mSnapAlgorithm;
public DividerView(Context context) {
super(context);
public boolean startDragging() {
mDockSide = mWindowManagerProxy.getDockSide();
+ mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
+ mDividerSize, isHorizontalDivision());
if (mDockSide != WindowManager.DOCKED_INVALID) {
mWindowManagerProxy.setResizing(true);
mWindowManager.setSlippery(false);
releaseBackground();
}
+ public DividerSnapAlgorithm getSnapAlgorithm() {
+ return mSnapAlgorithm;
+ }
+
@Override
public boolean onTouch(View v, MotionEvent event) {
convertToScreenCoordinates(event);
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:
}
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() {
}
}
- 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
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);
}
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);
}
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.*;
}
} 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());
}
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);
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.
private final ActivityMetricsLogger mActivityMetricsLogger;
+ private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
+
static class FindTaskResult {
ActivityRecord r;
boolean matchedByRootAffinity;
mRecentTasks = recentTasks;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
+ mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler);
}
/**
}
}
- 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;
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) {
}
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
--- /dev/null
+/*
+ * 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);
+ }
+
+}
// 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;
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;
}
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,
* @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;
}
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?");
* @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();
// 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
// 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) {
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()) {
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
// 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) {
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;
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