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