blob: d054e9a25ee13ba84587acf070888869845ba93f [file] [log] [blame]
/*
* Copyright (C) 2013 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.wm;
import android.graphics.Rect;
import android.util.Slog;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
import java.io.PrintWriter;
public class StackBox {
/** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
public static final int TASK_STACK_GOES_BEFORE = 0;
/** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
public static final int TASK_STACK_GOES_AFTER = 1;
/** Used with {@link WindowManagerService#createStack}. Horizontal to left of. */
public static final int TASK_STACK_TO_LEFT_OF = 2;
/** Used with {@link WindowManagerService#createStack}. Horizontal to right of. */
public static final int TASK_STACK_TO_RIGHT_OF = 3;
/** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
public static final int TASK_STACK_GOES_ABOVE = 4;
/** Used with {@link WindowManagerService#createStack}. Vertical: higher t/b Rect values. */
public static final int TASK_STACK_GOES_BELOW = 5;
/** Used with {@link WindowManagerService#createStack}. Put on a higher layer on display. */
public static final int TASK_STACK_GOES_OVER = 6;
/** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
public static final int TASK_STACK_GOES_UNDER = 7;
static int sCurrentBoxId = 0;
/** Unique id for this box */
final int mStackBoxId;
/** The service */
final WindowManagerService mService;
/** The display this box sits in. */
final DisplayContent mDisplayContent;
/** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
* is this entire size of mDisplayContent. */
StackBox mParent;
/** First child, this is null exactly when mStack is non-null. */
StackBox mFirst;
/** Second child, this is null exactly when mStack is non-null. */
StackBox mSecond;
/** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
TaskStack mStack;
/** Content limits relative to the DisplayContent this sits in. */
Rect mBounds = new Rect();
/** Relative orientation of mFirst and mSecond. */
boolean mVertical;
/** Fraction of mBounds to devote to mFirst, remainder goes to mSecond */
float mWeight;
/** Dirty flag. Something inside this or some descendant of this has changed. */
boolean layoutNeeded;
/** True if this StackBox sits below the Status Bar. */
boolean mUnderStatusBar;
/** Used to keep from reallocating a temporary Rect for propagating bounds to child boxes */
Rect mTmpRect = new Rect();
StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent) {
synchronized (StackBox.class) {
mStackBoxId = sCurrentBoxId++;
}
mService = service;
mDisplayContent = displayContent;
mParent = parent;
}
/** Propagate #layoutNeeded bottom up. */
void makeDirty() {
layoutNeeded = true;
if (mParent != null) {
mParent.makeDirty();
}
}
/**
* Determine if a particular StackBox is this one or a descendant of this one.
* @param stackBoxId The StackBox being searched for.
* @return true if the specified StackBox matches this or one of its descendants.
*/
boolean contains(int stackBoxId) {
return mStackBoxId == stackBoxId ||
(mStack == null && (mFirst.contains(stackBoxId) || mSecond.contains(stackBoxId)));
}
/**
* Return the stackId of the stack that intersects the passed point.
* @param x coordinate of point.
* @param y coordinate of point.
* @return -1 if point is outside of mBounds, otherwise the stackId of the containing stack.
*/
int stackIdFromPoint(int x, int y) {
if (!mBounds.contains(x, y)) {
return -1;
}
if (mStack != null) {
return mStack.mStackId;
}
int stackId = mFirst.stackIdFromPoint(x, y);
if (stackId >= 0) {
return stackId;
}
return mSecond.stackIdFromPoint(x, y);
}
/** Determine if this StackBox is the first child or second child.
* @return true if this is the first child.
*/
boolean isFirstChild() {
return mParent != null && mParent.mFirst == this;
}
/** Returns the bounds of the specified TaskStack if it is contained in this StackBox.
* @param stackId the TaskStack to find the bounds of.
* @return a new Rect with the bounds of stackId if it is within this StackBox, null otherwise.
*/
Rect getStackBounds(int stackId) {
if (mStack != null) {
return mStack.mStackId == stackId ? new Rect(mBounds) : null;
}
Rect bounds = mFirst.getStackBounds(stackId);
if (bounds != null) {
return bounds;
}
return mSecond.getStackBounds(stackId);
}
/**
* Create a new TaskStack relative to a specified one by splitting the StackBox containing
* the specified TaskStack into two children. The size and position each of the new StackBoxes
* is determined by the passed parameters.
* @param stackId The id of the new TaskStack to create.
* @param relativeStackBoxId The id of the StackBox to place the new TaskStack next to.
* @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
* @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
* @return The new TaskStack.
*/
TaskStack split(int stackId, int relativeStackBoxId, int position, float weight) {
if (mStackBoxId != relativeStackBoxId) {
// This is not the targeted StackBox.
if (mStack != null) {
return null;
}
// Propagate the split to see if the targeted StackBox is in either sub box.
TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight);
if (stack != null) {
return stack;
}
return mSecond.split(stackId, relativeStackBoxId, position, weight);
}
// Found it!
TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
TaskStack firstStack;
TaskStack secondStack;
if (position == TASK_STACK_GOES_BEFORE) {
// TODO: Test Configuration here for LTR/RTL.
position = TASK_STACK_TO_LEFT_OF;
} else if (position == TASK_STACK_GOES_AFTER) {
// TODO: Test Configuration here for LTR/RTL.
position = TASK_STACK_TO_RIGHT_OF;
}
switch (position) {
default:
case TASK_STACK_TO_LEFT_OF:
case TASK_STACK_TO_RIGHT_OF:
mVertical = false;
if (position == TASK_STACK_TO_LEFT_OF) {
mWeight = weight;
firstStack = stack;
secondStack = mStack;
} else {
mWeight = 1.0f - weight;
firstStack = mStack;
secondStack = stack;
}
break;
case TASK_STACK_GOES_ABOVE:
case TASK_STACK_GOES_BELOW:
mVertical = true;
if (position == TASK_STACK_GOES_ABOVE) {
mWeight = weight;
firstStack = stack;
secondStack = mStack;
} else {
mWeight = 1.0f - weight;
firstStack = mStack;
secondStack = stack;
}
break;
}
mFirst = new StackBox(mService, mDisplayContent, this);
firstStack.mStackBox = mFirst;
mFirst.mStack = firstStack;
mSecond = new StackBox(mService, mDisplayContent, this);
secondStack.mStackBox = mSecond;
mSecond.mStack = secondStack;
mStack = null;
return stack;
}
/** Return the stackId of the first mFirst StackBox with a non-null mStack */
int getStackId() {
if (mStack != null) {
return mStack.mStackId;
}
return mFirst.getStackId();
}
/** Remove this box and propagate its sibling's content up to their parent.
* @return The first stackId of the resulting StackBox. */
int remove() {
if (mStack != null) {
if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing stackId=" + mStack.mStackId);
mDisplayContent.mStackHistory.remove(mStack);
}
mDisplayContent.layoutNeeded = true;
if (mParent == null) {
// This is the top-plane stack.
if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing top plane.");
mDisplayContent.removeStackBox(this);
return HOME_STACK_ID;
}
StackBox sibling = isFirstChild() ? mParent.mSecond : mParent.mFirst;
StackBox grandparent = mParent.mParent;
sibling.mParent = grandparent;
if (grandparent == null) {
// mParent is a top-plane stack. Now sibling will be.
if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent null");
mDisplayContent.removeStackBox(mParent);
mDisplayContent.addStackBox(sibling, true);
} else {
if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent getting sibling");
if (mParent.isFirstChild()) {
grandparent.mFirst = sibling;
} else {
grandparent.mSecond = sibling;
}
}
return sibling.getStackId();
}
boolean resize(int stackBoxId, float weight) {
if (mStackBoxId != stackBoxId) {
return mStack == null &&
(mFirst.resize(stackBoxId, weight) || mSecond.resize(stackBoxId, weight));
}
// Don't change weight on topmost stack.
if (mParent != null) {
mParent.mWeight = isFirstChild() ? weight : 1.0f - weight;
}
return true;
}
/** If this is a terminal StackBox (contains a TaskStack) set the bounds.
* @param bounds The rectangle to set the bounds to.
* @param underStatusBar True if the StackBox is directly below the Status Bar.
* @return True if the bounds changed, false otherwise. */
boolean setStackBoxSizes(Rect bounds, boolean underStatusBar) {
boolean change = false;
if (mUnderStatusBar != underStatusBar) {
change = true;
mUnderStatusBar = underStatusBar;
}
if (mStack != null) {
change |= !mBounds.equals(bounds);
if (change) {
mBounds.set(bounds);
mStack.setBounds(bounds, underStatusBar);
}
} else {
mTmpRect.set(bounds);
if (mVertical) {
final int height = bounds.height();
int firstHeight = (int)(height * mWeight);
mTmpRect.bottom = bounds.top + firstHeight;
change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
mTmpRect.top = mTmpRect.bottom;
mTmpRect.bottom = bounds.top + height;
change |= mSecond.setStackBoxSizes(mTmpRect, false);
} else {
final int width = bounds.width();
int firstWidth = (int)(width * mWeight);
mTmpRect.right = bounds.left + firstWidth;
change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
mTmpRect.left = mTmpRect.right;
mTmpRect.right = bounds.left + width;
change |= mSecond.setStackBoxSizes(mTmpRect, underStatusBar);
}
}
return change;
}
void resetAnimationBackgroundAnimator() {
if (mStack != null) {
mStack.resetAnimationBackgroundAnimator();
return;
}
mFirst.resetAnimationBackgroundAnimator();
mSecond.resetAnimationBackgroundAnimator();
}
boolean animateDimLayers() {
if (mStack != null) {
return mStack.animateDimLayers();
}
boolean result = mFirst.animateDimLayers();
result |= mSecond.animateDimLayers();
return result;
}
void resetDimming() {
if (mStack != null) {
mStack.resetDimmingTag();
return;
}
mFirst.resetDimming();
mSecond.resetDimming();
}
boolean isDimming() {
if (mStack != null) {
return mStack.isDimming();
}
boolean result = mFirst.isDimming();
result |= mSecond.isDimming();
return result;
}
void stopDimmingIfNeeded() {
if (mStack != null) {
mStack.stopDimmingIfNeeded();
return;
}
mFirst.stopDimmingIfNeeded();
mSecond.stopDimmingIfNeeded();
}
void switchUserStacks(int userId) {
if (mStack != null) {
mStack.switchUser(userId);
return;
}
mFirst.switchUserStacks(userId);
mSecond.switchUserStacks(userId);
}
void close() {
if (mStack != null) {
mStack.mDimLayer.mDimSurface.destroy();
mStack.mAnimationBackgroundSurface.mDimSurface.destroy();
return;
}
mFirst.close();
mSecond.close();
}
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mParent="); pw.println(mParent);
pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
pw.print(" mVertical="); pw.print(mVertical);
pw.print(" layoutNeeded="); pw.println(layoutNeeded);
if (mFirst != null) {
pw.print(prefix); pw.print("mFirst="); pw.println(System.identityHashCode(mFirst));
mFirst.dump(prefix + " ", pw);
pw.print(prefix); pw.print("mSecond="); pw.println(System.identityHashCode(mSecond));
mSecond.dump(prefix + " ", pw);
} else {
pw.print(prefix); pw.print("mStack="); pw.println(mStack);
mStack.dump(prefix + " ", pw);
}
}
@Override
public String toString() {
if (mStack != null) {
return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}";
}
return "Box{" + hashCode() + " parent=" + System.identityHashCode(mParent)
+ " first=" + System.identityHashCode(mFirst)
+ " second=" + System.identityHashCode(mSecond) + "}";
}
}