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