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