Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2012 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.internal.widget;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.content.Context;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.res.Configuration;
     24 import android.content.res.TypedArray;
     25 import android.graphics.Canvas;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Build;
     29 import android.os.Parcelable;
     30 import android.util.AttributeSet;
     31 import android.util.IntProperty;
     32 import android.util.Log;
     33 import android.util.Property;
     34 import android.util.SparseArray;
     35 import android.view.Menu;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.view.ViewPropertyAnimator;
     39 import android.view.Window;
     40 import android.view.WindowInsets;
     41 import android.widget.OverScroller;
     42 import android.widget.Toolbar;
     43 import com.android.internal.view.menu.MenuPresenter;
     44 
     45 /**
     46  * Special layout for the containing of an overlay action bar (and its
     47  * content) to correctly handle fitting system windows when the content
     48  * has request that its layout ignore them.
     49  */
     50 public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent {
     51     private static final String TAG = "ActionBarOverlayLayout";
     52 
     53     private int mActionBarHeight;
     54     //private WindowDecorActionBar mActionBar;
     55     private int mWindowVisibility = View.VISIBLE;
     56 
     57     // The main UI elements that we handle the layout of.
     58     private View mContent;
     59     private ActionBarContainer mActionBarBottom;
     60     private ActionBarContainer mActionBarTop;
     61 
     62     // Some interior UI elements.
     63     private DecorToolbar mDecorToolbar;
     64 
     65     // Content overlay drawable - generally the action bar's shadow
     66     private Drawable mWindowContentOverlay;
     67     private boolean mIgnoreWindowContentOverlay;
     68 
     69     private boolean mOverlayMode;
     70     private boolean mHasNonEmbeddedTabs;
     71     private boolean mHideOnContentScroll;
     72     private boolean mAnimatingForFling;
     73     private int mHideOnContentScrollReference;
     74     private int mLastSystemUiVisibility;
     75     private final Rect mBaseContentInsets = new Rect();
     76     private final Rect mLastBaseContentInsets = new Rect();
     77     private final Rect mContentInsets = new Rect();
     78     private final Rect mBaseInnerInsets = new Rect();
     79     private final Rect mInnerInsets = new Rect();
     80     private final Rect mLastInnerInsets = new Rect();
     81 
     82     private ActionBarVisibilityCallback mActionBarVisibilityCallback;
     83 
     84     private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms
     85 
     86     private OverScroller mFlingEstimator;
     87 
     88     private ViewPropertyAnimator mCurrentActionBarTopAnimator;
     89     private ViewPropertyAnimator mCurrentActionBarBottomAnimator;
     90 
     91     private final Animator.AnimatorListener mTopAnimatorListener = new AnimatorListenerAdapter() {
     92         @Override
     93         public void onAnimationEnd(Animator animation) {
     94             mCurrentActionBarTopAnimator = null;
     95             mAnimatingForFling = false;
     96         }
     97 
     98         @Override
     99         public void onAnimationCancel(Animator animation) {
    100             mCurrentActionBarTopAnimator = null;
    101             mAnimatingForFling = false;
    102         }
    103     };
    104 
    105     private final Animator.AnimatorListener mBottomAnimatorListener =
    106             new AnimatorListenerAdapter() {
    107         @Override
    108         public void onAnimationEnd(Animator animation) {
    109             mCurrentActionBarBottomAnimator = null;
    110             mAnimatingForFling = false;
    111         }
    112 
    113         @Override
    114         public void onAnimationCancel(Animator animation) {
    115             mCurrentActionBarBottomAnimator = null;
    116             mAnimatingForFling = false;
    117         }
    118     };
    119 
    120     private final Runnable mRemoveActionBarHideOffset = new Runnable() {
    121         public void run() {
    122             haltActionBarHideOffsetAnimations();
    123             mCurrentActionBarTopAnimator = mActionBarTop.animate().translationY(0)
    124                     .setListener(mTopAnimatorListener);
    125             if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
    126                 mCurrentActionBarBottomAnimator = mActionBarBottom.animate().translationY(0)
    127                         .setListener(mBottomAnimatorListener);
    128             }
    129         }
    130     };
    131 
    132     private final Runnable mAddActionBarHideOffset = new Runnable() {
    133         public void run() {
    134             haltActionBarHideOffsetAnimations();
    135             mCurrentActionBarTopAnimator = mActionBarTop.animate()
    136                     .translationY(-mActionBarTop.getHeight())
    137                     .setListener(mTopAnimatorListener);
    138             if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
    139                 mCurrentActionBarBottomAnimator = mActionBarBottom.animate()
    140                         .translationY(mActionBarBottom.getHeight())
    141                         .setListener(mBottomAnimatorListener);
    142             }
    143         }
    144     };
    145 
    146     public static final Property<ActionBarOverlayLayout, Integer> ACTION_BAR_HIDE_OFFSET =
    147             new IntProperty<ActionBarOverlayLayout>("actionBarHideOffset") {
    148 
    149                 @Override
    150                 public void setValue(ActionBarOverlayLayout object, int value) {
    151                     object.setActionBarHideOffset(value);
    152                 }
    153 
    154                 @Override
    155                 public Integer get(ActionBarOverlayLayout object) {
    156                     return object.getActionBarHideOffset();
    157                 }
    158             };
    159 
    160     static final int[] ATTRS = new int [] {
    161             com.android.internal.R.attr.actionBarSize,
    162             com.android.internal.R.attr.windowContentOverlay
    163     };
    164 
    165     public ActionBarOverlayLayout(Context context) {
    166         super(context);
    167         init(context);
    168     }
    169 
    170     public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
    171         super(context, attrs);
    172         init(context);
    173     }
    174 
    175     private void init(Context context) {
    176         TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
    177         mActionBarHeight = ta.getDimensionPixelSize(0, 0);
    178         mWindowContentOverlay = ta.getDrawable(1);
    179         setWillNotDraw(mWindowContentOverlay == null);
    180         ta.recycle();
    181 
    182         mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
    183                 Build.VERSION_CODES.KITKAT;
    184 
    185         mFlingEstimator = new OverScroller(context);
    186     }
    187 
    188     @Override
    189     protected void onDetachedFromWindow() {
    190         super.onDetachedFromWindow();
    191         haltActionBarHideOffsetAnimations();
    192     }
    193 
    194     public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
    195         mActionBarVisibilityCallback = cb;
    196         if (getWindowToken() != null) {
    197             // This is being initialized after being added to a window;
    198             // make sure to update all state now.
    199             mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility);
    200             if (mLastSystemUiVisibility != 0) {
    201                 int newVis = mLastSystemUiVisibility;
    202                 onWindowSystemUiVisibilityChanged(newVis);
    203                 requestApplyInsets();
    204             }
    205         }
    206     }
    207 
    208     public void setOverlayMode(boolean overlayMode) {
    209         mOverlayMode = overlayMode;
    210 
    211         /*
    212          * Drawing the window content overlay was broken before K so starting to draw it
    213          * again unexpectedly will cause artifacts in some apps. They should fix it.
    214          */
    215         mIgnoreWindowContentOverlay = overlayMode &&
    216                 getContext().getApplicationInfo().targetSdkVersion <
    217                         Build.VERSION_CODES.KITKAT;
    218     }
    219 
    220     public boolean isInOverlayMode() {
    221         return mOverlayMode;
    222     }
    223 
    224     public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
    225         mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
    226     }
    227 
    228     public void setShowingForActionMode(boolean showing) {
    229         if (showing) {
    230             // Here's a fun hack: if the status bar is currently being hidden,
    231             // and the application has asked for stable content insets, then
    232             // we will end up with the action mode action bar being shown
    233             // without the status bar, but moved below where the status bar
    234             // would be.  Not nice.  Trying to have this be positioned
    235             // correctly is not easy (basically we need yet *another* content
    236             // inset from the window manager to know where to put it), so
    237             // instead we will just temporarily force the status bar to be shown.
    238             if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    239                     | SYSTEM_UI_FLAG_LAYOUT_STABLE))
    240                     == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
    241                 setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
    242             }
    243         } else {
    244             setDisabledSystemUiVisibility(0);
    245         }
    246     }
    247 
    248     @Override
    249     protected void onConfigurationChanged(Configuration newConfig) {
    250         super.onConfigurationChanged(newConfig);
    251         init(getContext());
    252         requestApplyInsets();
    253     }
    254 
    255     @Override
    256     public void onWindowSystemUiVisibilityChanged(int visible) {
    257         super.onWindowSystemUiVisibilityChanged(visible);
    258         pullChildren();
    259         final int diff = mLastSystemUiVisibility ^ visible;
    260         mLastSystemUiVisibility = visible;
    261         final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0;
    262         final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
    263         if (mActionBarVisibilityCallback != null) {
    264             // We want the bar to be visible if it is not being hidden,
    265             // or the app has not turned on a stable UI mode (meaning they
    266             // are performing explicit layout around the action bar).
    267             mActionBarVisibilityCallback.enableContentAnimations(!stable);
    268             if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem();
    269             else mActionBarVisibilityCallback.hideForSystem();
    270         }
    271         if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
    272             if (mActionBarVisibilityCallback != null) {
    273                 requestApplyInsets();
    274             }
    275         }
    276     }
    277 
    278     @Override
    279     protected void onWindowVisibilityChanged(int visibility) {
    280         super.onWindowVisibilityChanged(visibility);
    281         mWindowVisibility = visibility;
    282         if (mActionBarVisibilityCallback != null) {
    283             mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
    284         }
    285     }
    286 
    287     private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
    288             boolean bottom, boolean right) {
    289         boolean changed = false;
    290         LayoutParams lp = (LayoutParams)view.getLayoutParams();
    291         if (left && lp.leftMargin != insets.left) {
    292             changed = true;
    293             lp.leftMargin = insets.left;
    294         }
    295         if (top && lp.topMargin != insets.top) {
    296             changed = true;
    297             lp.topMargin = insets.top;
    298         }
    299         if (right && lp.rightMargin != insets.right) {
    300             changed = true;
    301             lp.rightMargin = insets.right;
    302         }
    303         if (bottom && lp.bottomMargin != insets.bottom) {
    304             changed = true;
    305             lp.bottomMargin = insets.bottom;
    306         }
    307         return changed;
    308     }
    309 
    310     @Override
    311     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    312         pullChildren();
    313 
    314         final int vis = getWindowSystemUiVisibility();
    315         final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
    316         final Rect systemInsets = insets.getSystemWindowInsets();
    317 
    318         // The top and bottom action bars are always within the content area.
    319         boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true);
    320         if (mActionBarBottom != null) {
    321             changed |= applyInsets(mActionBarBottom, systemInsets, true, false, true, true);
    322         }
    323 
    324         mBaseInnerInsets.set(systemInsets);
    325         computeFitSystemWindows(mBaseInnerInsets, mBaseContentInsets);
    326         if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
    327             changed = true;
    328             mLastBaseContentInsets.set(mBaseContentInsets);
    329         }
    330 
    331         if (changed) {
    332             requestLayout();
    333         }
    334 
    335         // We don't do any more at this point.  To correctly compute the content/inner
    336         // insets in all cases, we need to know the measured size of the various action
    337         // bar elements.  onApplyWindowInsets() happens before the measure pass, so we can't
    338         // do that here.  Instead we will take this up in onMeasure().
    339         return WindowInsets.CONSUMED;
    340     }
    341 
    342     @Override
    343     protected LayoutParams generateDefaultLayoutParams() {
    344         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    345     }
    346 
    347     @Override
    348     public LayoutParams generateLayoutParams(AttributeSet attrs) {
    349         return new LayoutParams(getContext(), attrs);
    350     }
    351 
    352     @Override
    353     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    354         return new LayoutParams(p);
    355     }
    356 
    357     @Override
    358     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    359         return p instanceof LayoutParams;
    360     }
    361 
    362     @Override
    363     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    364         pullChildren();
    365 
    366         int maxHeight = 0;
    367         int maxWidth = 0;
    368         int childState = 0;
    369 
    370         int topInset = 0;
    371         int bottomInset = 0;
    372 
    373         measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
    374         LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
    375         maxWidth = Math.max(maxWidth,
    376                 mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
    377         maxHeight = Math.max(maxHeight,
    378                 mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
    379         childState = combineMeasuredStates(childState, mActionBarTop.getMeasuredState());
    380 
    381         // xlarge screen layout doesn't have bottom action bar.
    382         if (mActionBarBottom != null) {
    383             measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0);
    384             lp = (LayoutParams) mActionBarBottom.getLayoutParams();
    385             maxWidth = Math.max(maxWidth,
    386                     mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
    387             maxHeight = Math.max(maxHeight,
    388                     mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
    389             childState = combineMeasuredStates(childState, mActionBarBottom.getMeasuredState());
    390         }
    391 
    392         final int vis = getWindowSystemUiVisibility();
    393         final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
    394 
    395         if (stable) {
    396             // This is the standard space needed for the action bar.  For stable measurement,
    397             // we can't depend on the size currently reported by it -- this must remain constant.
    398             topInset = mActionBarHeight;
    399             if (mHasNonEmbeddedTabs) {
    400                 final View tabs = mActionBarTop.getTabContainer();
    401                 if (tabs != null) {
    402                     // If tabs are not embedded, increase space on top to account for them.
    403                     topInset += mActionBarHeight;
    404                 }
    405             }
    406         } else if (mActionBarTop.getVisibility() != GONE) {
    407             // This is the space needed on top of the window for all of the action bar
    408             // and tabs.
    409             topInset = mActionBarTop.getMeasuredHeight();
    410         }
    411 
    412         if (mDecorToolbar.isSplit()) {
    413             // If action bar is split, adjust bottom insets for it.
    414             if (mActionBarBottom != null) {
    415                 if (stable) {
    416                     bottomInset = mActionBarHeight;
    417                 } else {
    418                     bottomInset = mActionBarBottom.getMeasuredHeight();
    419                 }
    420             }
    421         }
    422 
    423         // If the window has not requested system UI layout flags, we need to
    424         // make sure its content is not being covered by system UI...  though it
    425         // will still be covered by the action bar if they have requested it to
    426         // overlay.
    427         mContentInsets.set(mBaseContentInsets);
    428         mInnerInsets.set(mBaseInnerInsets);
    429         if (!mOverlayMode && !stable) {
    430             mContentInsets.top += topInset;
    431             mContentInsets.bottom += bottomInset;
    432         } else {
    433             mInnerInsets.top += topInset;
    434             mInnerInsets.bottom += bottomInset;
    435         }
    436         applyInsets(mContent, mContentInsets, true, true, true, true);
    437 
    438         if (!mLastInnerInsets.equals(mInnerInsets)) {
    439             // If the inner insets have changed, we need to dispatch this down to
    440             // the app's fitSystemWindows().  We do this before measuring the content
    441             // view to keep the same semantics as the normal fitSystemWindows() call.
    442             mLastInnerInsets.set(mInnerInsets);
    443             mContent.dispatchApplyWindowInsets(new WindowInsets(mInnerInsets));
    444         }
    445 
    446         measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
    447         lp = (LayoutParams) mContent.getLayoutParams();
    448         maxWidth = Math.max(maxWidth,
    449                 mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
    450         maxHeight = Math.max(maxHeight,
    451                 mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
    452         childState = combineMeasuredStates(childState, mContent.getMeasuredState());
    453 
    454         // Account for padding too
    455         maxWidth += getPaddingLeft() + getPaddingRight();
    456         maxHeight += getPaddingTop() + getPaddingBottom();
    457 
    458         // Check against our minimum height and width
    459         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    460         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
    461 
    462         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
    463                 resolveSizeAndState(maxHeight, heightMeasureSpec,
    464                         childState << MEASURED_HEIGHT_STATE_SHIFT));
    465     }
    466 
    467     @Override
    468     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    469         final int count = getChildCount();
    470 
    471         final int parentLeft = getPaddingLeft();
    472         final int parentRight = right - left - getPaddingRight();
    473 
    474         final int parentTop = getPaddingTop();
    475         final int parentBottom = bottom - top - getPaddingBottom();
    476 
    477         for (int i = 0; i < count; i++) {
    478             final View child = getChildAt(i);
    479             if (child.getVisibility() != GONE) {
    480                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    481 
    482                 final int width = child.getMeasuredWidth();
    483                 final int height = child.getMeasuredHeight();
    484 
    485                 int childLeft = parentLeft + lp.leftMargin;
    486                 int childTop;
    487                 if (child == mActionBarBottom) {
    488                     childTop = parentBottom - height - lp.bottomMargin;
    489                 } else {
    490                     childTop = parentTop + lp.topMargin;
    491                 }
    492 
    493                 child.layout(childLeft, childTop, childLeft + width, childTop + height);
    494             }
    495         }
    496     }
    497 
    498     @Override
    499     public void draw(Canvas c) {
    500         super.draw(c);
    501         if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
    502             final int top = mActionBarTop.getVisibility() == VISIBLE ?
    503                     (int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f) : 0;
    504             mWindowContentOverlay.setBounds(0, top, getWidth(),
    505                     top + mWindowContentOverlay.getIntrinsicHeight());
    506             mWindowContentOverlay.draw(c);
    507         }
    508     }
    509 
    510     @Override
    511     public boolean shouldDelayChildPressedState() {
    512         return false;
    513     }
    514 
    515     @Override
    516     public boolean onStartNestedScroll(View child, View target, int axes) {
    517         if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) {
    518             return false;
    519         }
    520         return mHideOnContentScroll;
    521     }
    522 
    523     @Override
    524     public void onNestedScrollAccepted(View child, View target, int axes) {
    525         super.onNestedScrollAccepted(child, target, axes);
    526         mHideOnContentScrollReference = getActionBarHideOffset();
    527         haltActionBarHideOffsetAnimations();
    528         if (mActionBarVisibilityCallback != null) {
    529             mActionBarVisibilityCallback.onContentScrollStarted();
    530         }
    531     }
    532 
    533     @Override
    534     public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
    535             int dxUnconsumed, int dyUnconsumed) {
    536         mHideOnContentScrollReference += dyConsumed;
    537         setActionBarHideOffset(mHideOnContentScrollReference);
    538     }
    539 
    540     @Override
    541     public void onStopNestedScroll(View target) {
    542         super.onStopNestedScroll(target);
    543         if (mHideOnContentScroll && !mAnimatingForFling) {
    544             if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) {
    545                 postRemoveActionBarHideOffset();
    546             } else {
    547                 postAddActionBarHideOffset();
    548             }
    549         }
    550         if (mActionBarVisibilityCallback != null) {
    551             mActionBarVisibilityCallback.onContentScrollStopped();
    552         }
    553     }
    554 
    555     @Override
    556     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
    557         if (!mHideOnContentScroll || !consumed) {
    558             return false;
    559         }
    560         if (shouldHideActionBarOnFling(velocityX, velocityY)) {
    561             addActionBarHideOffset();
    562         } else {
    563             removeActionBarHideOffset();
    564         }
    565         mAnimatingForFling = true;
    566         return true;
    567     }
    568 
    569     void pullChildren() {
    570         if (mContent == null) {
    571             mContent = findViewById(com.android.internal.R.id.content);
    572             mActionBarTop = (ActionBarContainer) findViewById(
    573                     com.android.internal.R.id.action_bar_container);
    574             mDecorToolbar = getDecorToolbar(findViewById(com.android.internal.R.id.action_bar));
    575             mActionBarBottom = (ActionBarContainer) findViewById(
    576                     com.android.internal.R.id.split_action_bar);
    577         }
    578     }
    579 
    580     private DecorToolbar getDecorToolbar(View view) {
    581         if (view instanceof DecorToolbar) {
    582             return (DecorToolbar) view;
    583         } else if (view instanceof Toolbar) {
    584             return ((Toolbar) view).getWrapper();
    585         } else {
    586             throw new IllegalStateException("Can't make a decor toolbar out of " +
    587                     view.getClass().getSimpleName());
    588         }
    589     }
    590 
    591     public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
    592         if (hideOnContentScroll != mHideOnContentScroll) {
    593             mHideOnContentScroll = hideOnContentScroll;
    594             if (!hideOnContentScroll) {
    595                 stopNestedScroll();
    596                 haltActionBarHideOffsetAnimations();
    597                 setActionBarHideOffset(0);
    598             }
    599         }
    600     }
    601 
    602     public boolean isHideOnContentScrollEnabled() {
    603         return mHideOnContentScroll;
    604     }
    605 
    606     public int getActionBarHideOffset() {
    607         return mActionBarTop != null ? -((int) mActionBarTop.getTranslationY()) : 0;
    608     }
    609 
    610     public void setActionBarHideOffset(int offset) {
    611         haltActionBarHideOffsetAnimations();
    612         final int topHeight = mActionBarTop.getHeight();
    613         offset = Math.max(0, Math.min(offset, topHeight));
    614         mActionBarTop.setTranslationY(-offset);
    615         if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
    616             // Match the hide offset proportionally for a split bar
    617             final float fOffset = (float) offset / topHeight;
    618             final int bOffset = (int) (mActionBarBottom.getHeight() * fOffset);
    619             mActionBarBottom.setTranslationY(bOffset);
    620         }
    621     }
    622 
    623     private void haltActionBarHideOffsetAnimations() {
    624         removeCallbacks(mRemoveActionBarHideOffset);
    625         removeCallbacks(mAddActionBarHideOffset);
    626         if (mCurrentActionBarTopAnimator != null) {
    627             mCurrentActionBarTopAnimator.cancel();
    628         }
    629         if (mCurrentActionBarBottomAnimator != null) {
    630             mCurrentActionBarBottomAnimator.cancel();
    631         }
    632     }
    633 
    634     private void postRemoveActionBarHideOffset() {
    635         haltActionBarHideOffsetAnimations();
    636         postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
    637     }
    638 
    639     private void postAddActionBarHideOffset() {
    640         haltActionBarHideOffsetAnimations();
    641         postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
    642     }
    643 
    644     private void removeActionBarHideOffset() {
    645         haltActionBarHideOffsetAnimations();
    646         mRemoveActionBarHideOffset.run();
    647     }
    648 
    649     private void addActionBarHideOffset() {
    650         haltActionBarHideOffsetAnimations();
    651         mAddActionBarHideOffset.run();
    652     }
    653 
    654     private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) {
    655         mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
    656         final int finalY = mFlingEstimator.getFinalY();
    657         return finalY > mActionBarTop.getHeight();
    658     }
    659 
    660     @Override
    661     public void setWindowCallback(Window.Callback cb) {
    662         pullChildren();
    663         mDecorToolbar.setWindowCallback(cb);
    664     }
    665 
    666     @Override
    667     public void setWindowTitle(CharSequence title) {
    668         pullChildren();
    669         mDecorToolbar.setWindowTitle(title);
    670     }
    671 
    672     @Override
    673     public CharSequence getTitle() {
    674         pullChildren();
    675         return mDecorToolbar.getTitle();
    676     }
    677 
    678     @Override
    679     public void initFeature(int windowFeature) {
    680         pullChildren();
    681         switch (windowFeature) {
    682             case Window.FEATURE_PROGRESS:
    683                 mDecorToolbar.initProgress();
    684                 break;
    685             case Window.FEATURE_INDETERMINATE_PROGRESS:
    686                 mDecorToolbar.initIndeterminateProgress();
    687                 break;
    688             case Window.FEATURE_ACTION_BAR_OVERLAY:
    689                 setOverlayMode(true);
    690                 break;
    691         }
    692     }
    693 
    694     @Override
    695     public void setUiOptions(int uiOptions) {
    696         boolean splitActionBar = false;
    697         final boolean splitWhenNarrow =
    698                 (uiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
    699         if (splitWhenNarrow) {
    700             splitActionBar = getContext().getResources().getBoolean(
    701                     com.android.internal.R.bool.split_action_bar_is_narrow);
    702         }
    703         if (splitActionBar) {
    704             pullChildren();
    705             if (mActionBarBottom != null && mDecorToolbar.canSplit()) {
    706                 mDecorToolbar.setSplitView(mActionBarBottom);
    707                 mDecorToolbar.setSplitToolbar(splitActionBar);
    708                 mDecorToolbar.setSplitWhenNarrow(splitWhenNarrow);
    709 
    710                 final ActionBarContextView cab = (ActionBarContextView) findViewById(
    711                         com.android.internal.R.id.action_context_bar);
    712                 cab.setSplitView(mActionBarBottom);
    713                 cab.setSplitToolbar(splitActionBar);
    714                 cab.setSplitWhenNarrow(splitWhenNarrow);
    715             } else if (splitActionBar) {
    716                 Log.e(TAG, "Requested split action bar with " +
    717                         "incompatible window decor! Ignoring request.");
    718             }
    719         }
    720     }
    721 
    722     @Override
    723     public boolean hasIcon() {
    724         pullChildren();
    725         return mDecorToolbar.hasIcon();
    726     }
    727 
    728     @Override
    729     public boolean hasLogo() {
    730         pullChildren();
    731         return mDecorToolbar.hasLogo();
    732     }
    733 
    734     @Override
    735     public void setIcon(int resId) {
    736         pullChildren();
    737         mDecorToolbar.setIcon(resId);
    738     }
    739 
    740     @Override
    741     public void setIcon(Drawable d) {
    742         pullChildren();
    743         mDecorToolbar.setIcon(d);
    744     }
    745 
    746     @Override
    747     public void setLogo(int resId) {
    748         pullChildren();
    749         mDecorToolbar.setLogo(resId);
    750     }
    751 
    752     @Override
    753     public boolean canShowOverflowMenu() {
    754         pullChildren();
    755         return mDecorToolbar.canShowOverflowMenu();
    756     }
    757 
    758     @Override
    759     public boolean isOverflowMenuShowing() {
    760         pullChildren();
    761         return mDecorToolbar.isOverflowMenuShowing();
    762     }
    763 
    764     @Override
    765     public boolean isOverflowMenuShowPending() {
    766         pullChildren();
    767         return mDecorToolbar.isOverflowMenuShowPending();
    768     }
    769 
    770     @Override
    771     public boolean showOverflowMenu() {
    772         pullChildren();
    773         return mDecorToolbar.showOverflowMenu();
    774     }
    775 
    776     @Override
    777     public boolean hideOverflowMenu() {
    778         pullChildren();
    779         return mDecorToolbar.hideOverflowMenu();
    780     }
    781 
    782     @Override
    783     public void setMenuPrepared() {
    784         pullChildren();
    785         mDecorToolbar.setMenuPrepared();
    786     }
    787 
    788     @Override
    789     public void setMenu(Menu menu, MenuPresenter.Callback cb) {
    790         pullChildren();
    791         mDecorToolbar.setMenu(menu, cb);
    792     }
    793 
    794     @Override
    795     public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
    796         pullChildren();
    797         mDecorToolbar.saveHierarchyState(toolbarStates);
    798     }
    799 
    800     @Override
    801     public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
    802         pullChildren();
    803         mDecorToolbar.restoreHierarchyState(toolbarStates);
    804     }
    805 
    806     @Override
    807     public void dismissPopups() {
    808         pullChildren();
    809         mDecorToolbar.dismissPopupMenus();
    810     }
    811 
    812     public static class LayoutParams extends MarginLayoutParams {
    813         public LayoutParams(Context c, AttributeSet attrs) {
    814             super(c, attrs);
    815         }
    816 
    817         public LayoutParams(int width, int height) {
    818             super(width, height);
    819         }
    820 
    821         public LayoutParams(ViewGroup.LayoutParams source) {
    822             super(source);
    823         }
    824 
    825         public LayoutParams(ViewGroup.MarginLayoutParams source) {
    826             super(source);
    827         }
    828     }
    829 
    830     public interface ActionBarVisibilityCallback {
    831         void onWindowVisibilityChanged(int visibility);
    832         void showForSystem();
    833         void hideForSystem();
    834         void enableContentAnimations(boolean enable);
    835         void onContentScrollStarted();
    836         void onContentScrollStopped();
    837     }
    838 }
    839