Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wm;
     18 
     19 import android.graphics.Rect;
     20 import android.util.Slog;
     21 
     22 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
     23 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
     24 import static com.android.server.wm.WindowManagerService.TAG;
     25 
     26 import java.io.PrintWriter;
     27 
     28 public class StackBox {
     29     /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
     30     public static final int TASK_STACK_GOES_BEFORE = 0;
     31     /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
     32     public static final int TASK_STACK_GOES_AFTER = 1;
     33     /** Used with {@link WindowManagerService#createStack}. Horizontal to left of. */
     34     public static final int TASK_STACK_TO_LEFT_OF = 2;
     35     /** Used with {@link WindowManagerService#createStack}. Horizontal to right of. */
     36     public static final int TASK_STACK_TO_RIGHT_OF = 3;
     37     /** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
     38     public static final int TASK_STACK_GOES_ABOVE = 4;
     39     /** Used with {@link WindowManagerService#createStack}. Vertical: higher t/b Rect values. */
     40     public static final int TASK_STACK_GOES_BELOW = 5;
     41     /** Used with {@link WindowManagerService#createStack}. Put on a higher layer on display. */
     42     public static final int TASK_STACK_GOES_OVER = 6;
     43     /** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
     44     public static final int TASK_STACK_GOES_UNDER = 7;
     45 
     46     static int sCurrentBoxId = 0;
     47 
     48     /** Unique id for this box */
     49     final int mStackBoxId;
     50 
     51     /** The service */
     52     final WindowManagerService mService;
     53 
     54     /** The display this box sits in. */
     55     final DisplayContent mDisplayContent;
     56 
     57     /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
     58      * is this entire size of mDisplayContent. */
     59     StackBox mParent;
     60 
     61     /** First child, this is null exactly when mStack is non-null. */
     62     StackBox mFirst;
     63 
     64     /** Second child, this is null exactly when mStack is non-null. */
     65     StackBox mSecond;
     66 
     67     /** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
     68     TaskStack mStack;
     69 
     70     /** Content limits relative to the DisplayContent this sits in. */
     71     Rect mBounds = new Rect();
     72 
     73     /** Relative orientation of mFirst and mSecond. */
     74     boolean mVertical;
     75 
     76     /** Fraction of mBounds to devote to mFirst, remainder goes to mSecond */
     77     float mWeight;
     78 
     79     /** Dirty flag. Something inside this or some descendant of this has changed. */
     80     boolean layoutNeeded;
     81 
     82     /** True if this StackBox sits below the Status Bar. */
     83     boolean mUnderStatusBar;
     84 
     85     /** Used to keep from reallocating a temporary Rect for propagating bounds to child boxes */
     86     Rect mTmpRect = new Rect();
     87 
     88     StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent) {
     89         synchronized (StackBox.class) {
     90             mStackBoxId = sCurrentBoxId++;
     91         }
     92 
     93         mService = service;
     94         mDisplayContent = displayContent;
     95         mParent = parent;
     96     }
     97 
     98     /** Propagate #layoutNeeded bottom up. */
     99     void makeDirty() {
    100         layoutNeeded = true;
    101         if (mParent != null) {
    102             mParent.makeDirty();
    103         }
    104     }
    105 
    106     /**
    107      * Determine if a particular StackBox is this one or a descendant of this one.
    108      * @param stackBoxId The StackBox being searched for.
    109      * @return true if the specified StackBox matches this or one of its descendants.
    110      */
    111     boolean contains(int stackBoxId) {
    112         return mStackBoxId == stackBoxId ||
    113                 (mStack == null &&  (mFirst.contains(stackBoxId) || mSecond.contains(stackBoxId)));
    114     }
    115 
    116     /**
    117      * Return the stackId of the stack that intersects the passed point.
    118      * @param x coordinate of point.
    119      * @param y coordinate of point.
    120      * @return -1 if point is outside of mBounds, otherwise the stackId of the containing stack.
    121      */
    122     int stackIdFromPoint(int x, int y) {
    123         if (!mBounds.contains(x, y)) {
    124             return -1;
    125         }
    126         if (mStack != null) {
    127             return mStack.mStackId;
    128         }
    129         int stackId = mFirst.stackIdFromPoint(x, y);
    130         if (stackId >= 0) {
    131             return stackId;
    132         }
    133         return mSecond.stackIdFromPoint(x, y);
    134     }
    135 
    136     /** Determine if this StackBox is the first child or second child.
    137      * @return true if this is the first child.
    138      */
    139     boolean isFirstChild() {
    140         return mParent != null && mParent.mFirst == this;
    141     }
    142 
    143     /** Returns the bounds of the specified TaskStack if it is contained in this StackBox.
    144      * @param stackId the TaskStack to find the bounds of.
    145      * @return a new Rect with the bounds of stackId if it is within this StackBox, null otherwise.
    146      */
    147     Rect getStackBounds(int stackId) {
    148         if (mStack != null) {
    149             return mStack.mStackId == stackId ? new Rect(mBounds) : null;
    150         }
    151         Rect bounds = mFirst.getStackBounds(stackId);
    152         if (bounds != null) {
    153             return bounds;
    154         }
    155         return mSecond.getStackBounds(stackId);
    156     }
    157 
    158     /**
    159      * Create a new TaskStack relative to a specified one by splitting the StackBox containing
    160      * the specified TaskStack into two children. The size and position each of the new StackBoxes
    161      * is determined by the passed parameters.
    162      * @param stackId The id of the new TaskStack to create.
    163      * @param relativeStackBoxId The id of the StackBox to place the new TaskStack next to.
    164      * @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
    165      * @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
    166      * @return The new TaskStack.
    167      */
    168     TaskStack split(int stackId, int relativeStackBoxId, int position, float weight) {
    169         if (mStackBoxId != relativeStackBoxId) {
    170             // This is not the targeted StackBox.
    171             if (mStack != null) {
    172                 return null;
    173             }
    174             // Propagate the split to see if the targeted StackBox is in either sub box.
    175             TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight);
    176             if (stack != null) {
    177                 return stack;
    178             }
    179             return mSecond.split(stackId, relativeStackBoxId, position, weight);
    180         }
    181 
    182         // Found it!
    183         TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
    184         TaskStack firstStack;
    185         TaskStack secondStack;
    186         if (position == TASK_STACK_GOES_BEFORE) {
    187             // TODO: Test Configuration here for LTR/RTL.
    188             position = TASK_STACK_TO_LEFT_OF;
    189         } else if (position == TASK_STACK_GOES_AFTER) {
    190             // TODO: Test Configuration here for LTR/RTL.
    191             position = TASK_STACK_TO_RIGHT_OF;
    192         }
    193         switch (position) {
    194             default:
    195             case TASK_STACK_TO_LEFT_OF:
    196             case TASK_STACK_TO_RIGHT_OF:
    197                 mVertical = false;
    198                 if (position == TASK_STACK_TO_LEFT_OF) {
    199                     mWeight = weight;
    200                     firstStack = stack;
    201                     secondStack = mStack;
    202                 } else {
    203                     mWeight = 1.0f - weight;
    204                     firstStack = mStack;
    205                     secondStack = stack;
    206                 }
    207                 break;
    208             case TASK_STACK_GOES_ABOVE:
    209             case TASK_STACK_GOES_BELOW:
    210                 mVertical = true;
    211                 if (position == TASK_STACK_GOES_ABOVE) {
    212                     mWeight = weight;
    213                     firstStack = stack;
    214                     secondStack = mStack;
    215                 } else {
    216                     mWeight = 1.0f - weight;
    217                     firstStack = mStack;
    218                     secondStack = stack;
    219                 }
    220                 break;
    221         }
    222 
    223         mFirst = new StackBox(mService, mDisplayContent, this);
    224         firstStack.mStackBox = mFirst;
    225         mFirst.mStack = firstStack;
    226 
    227         mSecond = new StackBox(mService, mDisplayContent, this);
    228         secondStack.mStackBox = mSecond;
    229         mSecond.mStack = secondStack;
    230 
    231         mStack = null;
    232         return stack;
    233     }
    234 
    235     /** Return the stackId of the first mFirst StackBox with a non-null mStack */
    236     int getStackId() {
    237         if (mStack != null) {
    238             return mStack.mStackId;
    239         }
    240         return mFirst.getStackId();
    241     }
    242 
    243     /** Remove this box and propagate its sibling's content up to their parent.
    244      * @return The first stackId of the resulting StackBox. */
    245     int remove() {
    246         mDisplayContent.layoutNeeded = true;
    247 
    248         if (mParent == null) {
    249             // This is the top-plane stack.
    250             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing top plane.");
    251             mDisplayContent.removeStackBox(this);
    252             return HOME_STACK_ID;
    253         }
    254 
    255         StackBox sibling = isFirstChild() ? mParent.mSecond : mParent.mFirst;
    256         StackBox grandparent = mParent.mParent;
    257         sibling.mParent = grandparent;
    258         if (grandparent == null) {
    259             // mParent is a top-plane stack. Now sibling will be.
    260             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent null");
    261             mDisplayContent.removeStackBox(mParent);
    262             mDisplayContent.addStackBox(sibling, true);
    263         } else {
    264             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent getting sibling");
    265             if (mParent.isFirstChild()) {
    266                 grandparent.mFirst = sibling;
    267             } else {
    268                 grandparent.mSecond = sibling;
    269             }
    270         }
    271         return sibling.getStackId();
    272     }
    273 
    274     boolean resize(int stackBoxId, float weight) {
    275         if (mStackBoxId != stackBoxId) {
    276             return mStack == null &&
    277                     (mFirst.resize(stackBoxId, weight) || mSecond.resize(stackBoxId, weight));
    278         }
    279         // Don't change weight on topmost stack.
    280         if (mParent != null) {
    281             mParent.mWeight = isFirstChild() ? weight : 1.0f - weight;
    282         }
    283         return true;
    284     }
    285 
    286     /** If this is a terminal StackBox (contains a TaskStack) set the bounds.
    287      * @param bounds The rectangle to set the bounds to.
    288      * @param underStatusBar True if the StackBox is directly below the Status Bar.
    289      * @return True if the bounds changed, false otherwise. */
    290     boolean setStackBoxSizes(Rect bounds, boolean underStatusBar) {
    291         boolean change = false;
    292         if (mUnderStatusBar != underStatusBar) {
    293             change = true;
    294             mUnderStatusBar = underStatusBar;
    295         }
    296         if (mStack != null) {
    297             change |= !mBounds.equals(bounds);
    298             if (change) {
    299                 mBounds.set(bounds);
    300                 mStack.setBounds(bounds, underStatusBar);
    301             }
    302         } else {
    303             mTmpRect.set(bounds);
    304             if (mVertical) {
    305                 final int height = bounds.height();
    306                 int firstHeight = (int)(height * mWeight);
    307                 mTmpRect.bottom = bounds.top + firstHeight;
    308                 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
    309                 mTmpRect.top = mTmpRect.bottom;
    310                 mTmpRect.bottom = bounds.top + height;
    311                 change |= mSecond.setStackBoxSizes(mTmpRect, false);
    312             } else {
    313                 final int width = bounds.width();
    314                 int firstWidth = (int)(width * mWeight);
    315                 mTmpRect.right = bounds.left + firstWidth;
    316                 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
    317                 mTmpRect.left = mTmpRect.right;
    318                 mTmpRect.right = bounds.left + width;
    319                 change |= mSecond.setStackBoxSizes(mTmpRect, underStatusBar);
    320             }
    321         }
    322         return change;
    323     }
    324 
    325     void resetAnimationBackgroundAnimator() {
    326         if (mStack != null) {
    327             mStack.resetAnimationBackgroundAnimator();
    328             return;
    329         }
    330         mFirst.resetAnimationBackgroundAnimator();
    331         mSecond.resetAnimationBackgroundAnimator();
    332     }
    333 
    334     boolean animateDimLayers() {
    335         if (mStack != null) {
    336             return mStack.animateDimLayers();
    337         }
    338         boolean result = mFirst.animateDimLayers();
    339         result |= mSecond.animateDimLayers();
    340         return result;
    341     }
    342 
    343     void resetDimming() {
    344         if (mStack != null) {
    345             mStack.resetDimmingTag();
    346             return;
    347         }
    348         mFirst.resetDimming();
    349         mSecond.resetDimming();
    350     }
    351 
    352     boolean isDimming() {
    353         if (mStack != null) {
    354             return mStack.isDimming();
    355         }
    356         boolean result = mFirst.isDimming();
    357         result |= mSecond.isDimming();
    358         return result;
    359     }
    360 
    361     void stopDimmingIfNeeded() {
    362         if (mStack != null) {
    363             mStack.stopDimmingIfNeeded();
    364             return;
    365         }
    366         mFirst.stopDimmingIfNeeded();
    367         mSecond.stopDimmingIfNeeded();
    368     }
    369 
    370     void switchUserStacks(int userId) {
    371         if (mStack != null) {
    372             mStack.switchUser(userId);
    373             return;
    374         }
    375         mFirst.switchUserStacks(userId);
    376         mSecond.switchUserStacks(userId);
    377     }
    378 
    379     void close() {
    380         if (mStack != null) {
    381             mStack.mDimLayer.mDimSurface.destroy();
    382             mStack.mAnimationBackgroundSurface.mDimSurface.destroy();
    383             return;
    384         }
    385         mFirst.close();
    386         mSecond.close();
    387     }
    388 
    389     public void dump(String prefix, PrintWriter pw) {
    390         pw.print(prefix); pw.print("mParent="); pw.println(mParent);
    391         pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
    392             pw.print(" mVertical="); pw.print(mVertical);
    393             pw.print(" layoutNeeded="); pw.println(layoutNeeded);
    394         if (mFirst != null) {
    395             pw.print(prefix); pw.print("mFirst="); pw.println(System.identityHashCode(mFirst));
    396             mFirst.dump(prefix + "  ", pw);
    397             pw.print(prefix); pw.print("mSecond="); pw.println(System.identityHashCode(mSecond));
    398             mSecond.dump(prefix + "  ", pw);
    399         } else {
    400             pw.print(prefix); pw.print("mStack="); pw.println(mStack);
    401             mStack.dump(prefix + "  ", pw);
    402         }
    403     }
    404 
    405     @Override
    406     public String toString() {
    407         if (mStack != null) {
    408             return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}";
    409         }
    410         return "Box{" + hashCode() + " parent=" + System.identityHashCode(mParent)
    411                 + " first=" + System.identityHashCode(mFirst)
    412                 + " second=" + System.identityHashCode(mSecond) + "}";
    413     }
    414 }
    415