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