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