blob: 4bdb40697c35256cf47d95b2cb2b38714efa772c [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 java.io.PrintWriter;
import java.util.ArrayList;
public class StackBox {
/** For use with {@link WindowManagerService#createStack} */
public static final int TASK_STACK_GOES_BEFORE = 0;
public static final int TASK_STACK_GOES_AFTER = 1;
public static final int TASK_STACK_GOES_ABOVE = 2;
public static final int TASK_STACK_GOES_BELOW = 3;
public static final int TASK_STACK_GOES_OVER = 4;
public static final int TASK_STACK_GOES_UNDER = 5;
/** 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;
/** Relative orientation of mFirst and mSecond. */
boolean mVertical;
/** Dirty flag. Something inside this or some descendant of this has changed. */
boolean layoutNeeded;
/** Used to keep from reallocating a temporary array to hold the list of Tasks below */
ArrayList<Task> mTmpTasks = new ArrayList<Task>();
StackBox(DisplayContent displayContent, Rect bounds) {
mDisplayContent = displayContent;
mBounds = bounds;
}
/** Propagate #layoutNeeded bottom up. */
void makeDirty() {
layoutNeeded = true;
if (mParent != null) {
mParent.makeDirty();
}
}
/** Propagate #layoutNeeded top down. */
void makeClean() {
layoutNeeded = false;
if (mFirst != null) {
mFirst.makeClean();
mSecond.makeClean();
}
}
/**
* Detremine if a particular TaskStack is in this StackBox or any of its descendants.
* @param stackId The TaskStack being considered.
* @return true if the specified TaskStack is in this box or its descendants. False otherwise.
*/
boolean contains(int stackId) {
if (mStack != null) {
return mStack.mStackId == stackId;
}
return mFirst.contains(stackId) || mSecond.contains(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 relativeStackId The id of the TaskStack to place the new one 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 relativeStackId, int position, float weight) {
if (mStack != null) {
if (mStack.mStackId == relativeStackId) {
// Found it!
TaskStack stack = new TaskStack(stackId, this);
TaskStack firstStack;
TaskStack secondStack;
int width, height, split;
switch (position) {
default:
case TASK_STACK_GOES_BEFORE:
case TASK_STACK_GOES_AFTER:
mVertical = false;
width = (int)(weight * mBounds.width());
height = mBounds.height();
if (position == TASK_STACK_GOES_BEFORE) {
firstStack = stack;
secondStack = mStack;
split = mBounds.left + width;
} else {
firstStack = mStack;
secondStack = stack;
split = mBounds.right - width;
}
break;
case TASK_STACK_GOES_ABOVE:
case TASK_STACK_GOES_BELOW:
mVertical = true;
width = mBounds.width();
height = (int)(weight * mBounds.height());
if (position == TASK_STACK_GOES_ABOVE) {
firstStack = stack;
secondStack = mStack;
split = mBounds.top + height;
} else {
firstStack = mStack;
secondStack = stack;
split = mBounds.bottom - height;
}
break;
}
mFirst = new StackBox(mDisplayContent, new Rect(mBounds.left, mBounds.top,
mVertical ? mBounds.right : split, mVertical ? split : mBounds.bottom));
mFirst.mStack = firstStack;
mSecond = new StackBox(mDisplayContent, new Rect(mVertical ? mBounds.left : split,
mVertical ? split : mBounds.top, mBounds.right, mBounds.bottom));
mSecond.mStack = secondStack;
mStack = null;
return stack;
}
// Not the intended TaskStack.
return null;
}
// Propagate the split to see if the target task stack is in either sub box.
TaskStack stack = mFirst.split(stackId, relativeStackId, position, weight);
if (stack != null) {
return stack;
}
return mSecond.split(stackId, relativeStackId, position, weight);
}
/**
* @return List of all Tasks underneath this StackBox. The order is currently mFirst followed
* by mSecond putting mSecond Tasks more recent than mFirst Tasks.
* TODO: Change to MRU ordering.
*/
ArrayList<Task> getTasks() {
mTmpTasks.clear();
if (mStack != null) {
mTmpTasks.addAll(mStack.getTasks());
} else {
mTmpTasks.addAll(mFirst.getTasks());
mTmpTasks.addAll(mSecond.getTasks());
}
return mTmpTasks;
}
/** Combine a child StackBox into its parent.
* @param child The surviving child to be merge up into this StackBox. */
void absorb(StackBox child) {
mFirst = child.mFirst;
mSecond = child.mSecond;
mStack = child.mStack;
layoutNeeded = true;
}
/** 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 removeStack() {
if (mParent.mFirst == this) {
mParent.absorb(mParent.mSecond);
} else {
mParent.absorb(mParent.mFirst);
}
mParent.makeDirty();
return getStackId();
}
/** TODO: */
boolean resize(int stackId, float weight) {
return false;
}
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mParent="); pw.println(mParent);
pw.print(prefix); pw.print("mFirst="); pw.println(mFirst);
pw.print(prefix); pw.print("mSecond="); pw.println(mSecond);
pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
pw.print("mVertical="); pw.print(mVertical);
pw.print("layoutNeeded="); pw.println(layoutNeeded);
if (mStack != null) {
pw.print(prefix); pw.print("mStack="); pw.println(mStack);
mStack.dump(prefix + " ", pw);
} else {
pw.print(prefix); pw.print("mFirst="); pw.println(mStack);
mFirst.dump(prefix + " ", pw);
pw.print(prefix); pw.print("mSecond="); pw.println(mStack);
mSecond.dump(prefix + " ", pw);
}
}
}