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