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