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