Source code
package android.support.v7.internal.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Parcelable;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
import android.support.v4.widget.ScrollerCompat;
import android.support.v7.appcompat.R;
import android.support.v7.internal.view.menu.MenuPresenter;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window.Callback;
public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent, NestedScrollingParent {
static final int[] ATTRS = new int[]{R.attr.actionBarSize, 16842841};
private static final String TAG = "ActionBarOverlayLayout";
private final int ACTION_BAR_ANIMATE_DELAY;
private int mActionBarHeight;
private ActionBarContainer mActionBarTop;
private ActionBarVisibilityCallback mActionBarVisibilityCallback;
private final Runnable mAddActionBarHideOffset;
private boolean mAnimatingForFling;
private final Rect mBaseContentInsets;
private final Rect mBaseInnerInsets;
private ContentFrameLayout mContent;
private final Rect mContentInsets;
private ViewPropertyAnimatorCompat mCurrentActionBarTopAnimator;
private DecorToolbar mDecorToolbar;
private ScrollerCompat mFlingEstimator;
private boolean mHasNonEmbeddedTabs;
private boolean mHideOnContentScroll;
private int mHideOnContentScrollReference;
private boolean mIgnoreWindowContentOverlay;
private final Rect mInnerInsets;
private final Rect mLastBaseContentInsets;
private final Rect mLastInnerInsets;
private int mLastSystemUiVisibility;
private boolean mOverlayMode;
private final NestedScrollingParentHelper mParentHelper;
private final Runnable mRemoveActionBarHideOffset;
private final ViewPropertyAnimatorListener mTopAnimatorListener;
private Drawable mWindowContentOverlay;
private int mWindowVisibility;
public interface ActionBarVisibilityCallback {
void enableContentAnimations(boolean z);
void hideForSystem();
void onContentScrollStarted();
void onContentScrollStopped();
void onWindowVisibilityChanged(int i);
void showForSystem();
}
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(android.view.ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
public ActionBarOverlayLayout(Context context) {
this(context, null);
}
public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.mWindowVisibility = 0;
this.mBaseContentInsets = new Rect();
this.mLastBaseContentInsets = new Rect();
this.mContentInsets = new Rect();
this.mBaseInnerInsets = new Rect();
this.mInnerInsets = new Rect();
this.mLastInnerInsets = new Rect();
this.ACTION_BAR_ANIMATE_DELAY = 600;
this.mTopAnimatorListener = new ViewPropertyAnimatorListenerAdapter() {
public void onAnimationEnd(View view) {
ActionBarOverlayLayout.this.mCurrentActionBarTopAnimator = null;
ActionBarOverlayLayout.this.mAnimatingForFling = false;
}
public void onAnimationCancel(View view) {
ActionBarOverlayLayout.this.mCurrentActionBarTopAnimator = null;
ActionBarOverlayLayout.this.mAnimatingForFling = false;
}
};
this.mRemoveActionBarHideOffset = new Runnable() {
public void run() {
ActionBarOverlayLayout.this.haltActionBarHideOffsetAnimations();
ActionBarOverlayLayout.this.mCurrentActionBarTopAnimator = ViewCompat.animate(ActionBarOverlayLayout.this.mActionBarTop).translationY(0.0f).setListener(ActionBarOverlayLayout.this.mTopAnimatorListener);
}
};
this.mAddActionBarHideOffset = new Runnable() {
public void run() {
ActionBarOverlayLayout.this.haltActionBarHideOffsetAnimations();
ActionBarOverlayLayout.this.mCurrentActionBarTopAnimator = ViewCompat.animate(ActionBarOverlayLayout.this.mActionBarTop).translationY((float) (-ActionBarOverlayLayout.this.mActionBarTop.getHeight())).setListener(ActionBarOverlayLayout.this.mTopAnimatorListener);
}
};
init(context);
this.mParentHelper = new NestedScrollingParentHelper(this);
}
private void init(Context context) {
boolean z = true;
TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
this.mActionBarHeight = ta.getDimensionPixelSize(0, 0);
this.mWindowContentOverlay = ta.getDrawable(1);
setWillNotDraw(this.mWindowContentOverlay == null);
ta.recycle();
if (context.getApplicationInfo().targetSdkVersion >= 19) {
z = false;
}
this.mIgnoreWindowContentOverlay = z;
this.mFlingEstimator = ScrollerCompat.create(context);
}
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
haltActionBarHideOffsetAnimations();
}
public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
this.mActionBarVisibilityCallback = cb;
if (getWindowToken() != null) {
this.mActionBarVisibilityCallback.onWindowVisibilityChanged(this.mWindowVisibility);
if (this.mLastSystemUiVisibility != 0) {
onWindowSystemUiVisibilityChanged(this.mLastSystemUiVisibility);
ViewCompat.requestApplyInsets(this);
}
}
}
public void setOverlayMode(boolean overlayMode) {
this.mOverlayMode = overlayMode;
boolean z = overlayMode && getContext().getApplicationInfo().targetSdkVersion < 19;
this.mIgnoreWindowContentOverlay = z;
}
public boolean isInOverlayMode() {
return this.mOverlayMode;
}
public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
this.mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
}
public void setShowingForActionMode(boolean showing) {
}
protected void onConfigurationChanged(Configuration newConfig) {
if (VERSION.SDK_INT >= 8) {
super.onConfigurationChanged(newConfig);
}
init(getContext());
ViewCompat.requestApplyInsets(this);
}
public void onWindowSystemUiVisibilityChanged(int visible) {
boolean barVisible;
boolean stable;
boolean z = true;
if (VERSION.SDK_INT >= 16) {
super.onWindowSystemUiVisibilityChanged(visible);
}
pullChildren();
int diff = this.mLastSystemUiVisibility ^ visible;
this.mLastSystemUiVisibility = visible;
if ((visible & 4) == 0) {
barVisible = true;
} else {
barVisible = false;
}
if ((visible & 256) != 0) {
stable = true;
} else {
stable = false;
}
if (this.mActionBarVisibilityCallback != null) {
ActionBarVisibilityCallback actionBarVisibilityCallback = this.mActionBarVisibilityCallback;
if (stable) {
z = false;
}
actionBarVisibilityCallback.enableContentAnimations(z);
if (barVisible || !stable) {
this.mActionBarVisibilityCallback.showForSystem();
} else {
this.mActionBarVisibilityCallback.hideForSystem();
}
}
if ((diff & 256) != 0 && this.mActionBarVisibilityCallback != null) {
ViewCompat.requestApplyInsets(this);
}
}
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
this.mWindowVisibility = visibility;
if (this.mActionBarVisibilityCallback != null) {
this.mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
}
}
private boolean applyInsets(View view, Rect insets, boolean left, boolean top, boolean bottom, boolean right) {
boolean changed = false;
LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (left && lp.leftMargin != insets.left) {
changed = true;
lp.leftMargin = insets.left;
}
if (top && lp.topMargin != insets.top) {
changed = true;
lp.topMargin = insets.top;
}
if (right && lp.rightMargin != insets.right) {
changed = true;
lp.rightMargin = insets.right;
}
if (!bottom || lp.bottomMargin == insets.bottom) {
return changed;
}
lp.bottomMargin = insets.bottom;
return true;
}
protected boolean fitSystemWindows(Rect insets) {
pullChildren();
boolean stable;
if ((ViewCompat.getWindowSystemUiVisibility(this) & 256) != 0) {
stable = true;
} else {
stable = false;
}
Rect systemInsets = insets;
boolean changed = applyInsets(this.mActionBarTop, systemInsets, true, true, false, true);
this.mBaseInnerInsets.set(systemInsets);
ViewUtils.computeFitSystemWindows(this, this.mBaseInnerInsets, this.mBaseContentInsets);
if (!this.mLastBaseContentInsets.equals(this.mBaseContentInsets)) {
changed = true;
this.mLastBaseContentInsets.set(this.mBaseContentInsets);
}
if (changed) {
requestLayout();
}
return true;
}
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(-1, -1);
}
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
protected android.view.ViewGroup.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
pullChildren();
int topInset = 0;
measureChildWithMargins(this.mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
LayoutParams lp = (LayoutParams) this.mActionBarTop.getLayoutParams();
int maxWidth = Math.max(0, (this.mActionBarTop.getMeasuredWidth() + lp.leftMargin) + lp.rightMargin);
int maxHeight = Math.max(0, (this.mActionBarTop.getMeasuredHeight() + lp.topMargin) + lp.bottomMargin);
int childState = ViewUtils.combineMeasuredStates(0, ViewCompat.getMeasuredState(this.mActionBarTop));
boolean stable = (ViewCompat.getWindowSystemUiVisibility(this) & 256) != 0;
if (stable) {
topInset = this.mActionBarHeight;
if (this.mHasNonEmbeddedTabs && this.mActionBarTop.getTabContainer() != null) {
topInset += this.mActionBarHeight;
}
} else if (this.mActionBarTop.getVisibility() != 8) {
topInset = this.mActionBarTop.getMeasuredHeight();
}
this.mContentInsets.set(this.mBaseContentInsets);
this.mInnerInsets.set(this.mBaseInnerInsets);
Rect rect;
if (this.mOverlayMode || stable) {
rect = this.mInnerInsets;
rect.top += topInset;
rect = this.mInnerInsets;
rect.bottom += 0;
} else {
rect = this.mContentInsets;
rect.top += topInset;
rect = this.mContentInsets;
rect.bottom += 0;
}
applyInsets(this.mContent, this.mContentInsets, true, true, true, true);
if (!this.mLastInnerInsets.equals(this.mInnerInsets)) {
this.mLastInnerInsets.set(this.mInnerInsets);
this.mContent.dispatchFitSystemWindows(this.mInnerInsets);
}
measureChildWithMargins(this.mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
lp = (LayoutParams) this.mContent.getLayoutParams();
maxWidth = Math.max(maxWidth, (this.mContent.getMeasuredWidth() + lp.leftMargin) + lp.rightMargin);
maxHeight = Math.max(maxHeight, (this.mContent.getMeasuredHeight() + lp.topMargin) + lp.bottomMargin);
childState = ViewUtils.combineMeasuredStates(childState, ViewCompat.getMeasuredState(this.mContent));
setMeasuredDimension(ViewCompat.resolveSizeAndState(Math.max(maxWidth + (getPaddingLeft() + getPaddingRight()), getSuggestedMinimumWidth()), widthMeasureSpec, childState), ViewCompat.resolveSizeAndState(Math.max(maxHeight + (getPaddingTop() + getPaddingBottom()), getSuggestedMinimumHeight()), heightMeasureSpec, childState << 16));
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
int parentLeft = getPaddingLeft();
int parentRight = (right - left) - getPaddingRight();
int parentTop = getPaddingTop();
int parentBottom = (bottom - top) - getPaddingBottom();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != 8) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childLeft = parentLeft + lp.leftMargin;
int childTop = parentTop + lp.topMargin;
child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
}
}
}
public void draw(Canvas c) {
super.draw(c);
if (this.mWindowContentOverlay != null && !this.mIgnoreWindowContentOverlay) {
int top = this.mActionBarTop.getVisibility() == 0 ? (int) ((((float) this.mActionBarTop.getBottom()) + ViewCompat.getTranslationY(this.mActionBarTop)) + 0.5f) : 0;
this.mWindowContentOverlay.setBounds(0, top, getWidth(), this.mWindowContentOverlay.getIntrinsicHeight() + top);
this.mWindowContentOverlay.draw(c);
}
}
public boolean shouldDelayChildPressedState() {
return false;
}
public boolean onStartNestedScroll(View child, View target, int axes) {
if ((axes & 2) == 0 || this.mActionBarTop.getVisibility() != 0) {
return false;
}
return this.mHideOnContentScroll;
}
public void onNestedScrollAccepted(View child, View target, int axes) {
this.mParentHelper.onNestedScrollAccepted(child, target, axes);
this.mHideOnContentScrollReference = getActionBarHideOffset();
haltActionBarHideOffsetAnimations();
if (this.mActionBarVisibilityCallback != null) {
this.mActionBarVisibilityCallback.onContentScrollStarted();
}
}
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
this.mHideOnContentScrollReference += dyConsumed;
setActionBarHideOffset(this.mHideOnContentScrollReference);
}
public void onStopNestedScroll(View target) {
if (this.mHideOnContentScroll && !this.mAnimatingForFling) {
if (this.mHideOnContentScrollReference <= this.mActionBarTop.getHeight()) {
postRemoveActionBarHideOffset();
} else {
postAddActionBarHideOffset();
}
}
if (this.mActionBarVisibilityCallback != null) {
this.mActionBarVisibilityCallback.onContentScrollStopped();
}
}
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
if (!this.mHideOnContentScroll || !consumed) {
return false;
}
if (shouldHideActionBarOnFling(velocityX, velocityY)) {
addActionBarHideOffset();
} else {
removeActionBarHideOffset();
}
this.mAnimatingForFling = true;
return true;
}
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
}
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
}
public int getNestedScrollAxes() {
return this.mParentHelper.getNestedScrollAxes();
}
void pullChildren() {
if (this.mContent == null) {
this.mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content);
this.mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container);
this.mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar));
}
}
private DecorToolbar getDecorToolbar(View view) {
if (view instanceof DecorToolbar) {
return (DecorToolbar) view;
}
if (view instanceof Toolbar) {
return ((Toolbar) view).getWrapper();
}
throw new IllegalStateException("Can't make a decor toolbar out of " + view.getClass().getSimpleName());
}
public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
if (hideOnContentScroll != this.mHideOnContentScroll) {
this.mHideOnContentScroll = hideOnContentScroll;
if (!hideOnContentScroll) {
haltActionBarHideOffsetAnimations();
setActionBarHideOffset(0);
}
}
}
public boolean isHideOnContentScrollEnabled() {
return this.mHideOnContentScroll;
}
public int getActionBarHideOffset() {
return this.mActionBarTop != null ? -((int) ViewCompat.getTranslationY(this.mActionBarTop)) : 0;
}
public void setActionBarHideOffset(int offset) {
haltActionBarHideOffsetAnimations();
ViewCompat.setTranslationY(this.mActionBarTop, (float) (-Math.max(0, Math.min(offset, this.mActionBarTop.getHeight()))));
}
private void haltActionBarHideOffsetAnimations() {
removeCallbacks(this.mRemoveActionBarHideOffset);
removeCallbacks(this.mAddActionBarHideOffset);
if (this.mCurrentActionBarTopAnimator != null) {
this.mCurrentActionBarTopAnimator.cancel();
}
}
private void postRemoveActionBarHideOffset() {
haltActionBarHideOffsetAnimations();
postDelayed(this.mRemoveActionBarHideOffset, 600);
}
private void postAddActionBarHideOffset() {
haltActionBarHideOffsetAnimations();
postDelayed(this.mAddActionBarHideOffset, 600);
}
private void removeActionBarHideOffset() {
haltActionBarHideOffsetAnimations();
this.mRemoveActionBarHideOffset.run();
}
private void addActionBarHideOffset() {
haltActionBarHideOffsetAnimations();
this.mAddActionBarHideOffset.run();
}
private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) {
this.mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
if (this.mFlingEstimator.getFinalY() > this.mActionBarTop.getHeight()) {
return true;
}
return false;
}
public void setWindowCallback(Callback cb) {
pullChildren();
this.mDecorToolbar.setWindowCallback(cb);
}
public void setWindowTitle(CharSequence title) {
pullChildren();
this.mDecorToolbar.setWindowTitle(title);
}
public CharSequence getTitle() {
pullChildren();
return this.mDecorToolbar.getTitle();
}
public void initFeature(int windowFeature) {
pullChildren();
switch (windowFeature) {
case 2:
this.mDecorToolbar.initProgress();
return;
case 5:
this.mDecorToolbar.initIndeterminateProgress();
return;
case 109:
setOverlayMode(true);
return;
default:
return;
}
}
public void setUiOptions(int uiOptions) {
}
public boolean hasIcon() {
pullChildren();
return this.mDecorToolbar.hasIcon();
}
public boolean hasLogo() {
pullChildren();
return this.mDecorToolbar.hasLogo();
}
public void setIcon(int resId) {
pullChildren();
this.mDecorToolbar.setIcon(resId);
}
public void setIcon(Drawable d) {
pullChildren();
this.mDecorToolbar.setIcon(d);
}
public void setLogo(int resId) {
pullChildren();
this.mDecorToolbar.setLogo(resId);
}
public boolean canShowOverflowMenu() {
pullChildren();
return this.mDecorToolbar.canShowOverflowMenu();
}
public boolean isOverflowMenuShowing() {
pullChildren();
return this.mDecorToolbar.isOverflowMenuShowing();
}
public boolean isOverflowMenuShowPending() {
pullChildren();
return this.mDecorToolbar.isOverflowMenuShowPending();
}
public boolean showOverflowMenu() {
pullChildren();
return this.mDecorToolbar.showOverflowMenu();
}
public boolean hideOverflowMenu() {
pullChildren();
return this.mDecorToolbar.hideOverflowMenu();
}
public void setMenuPrepared() {
pullChildren();
this.mDecorToolbar.setMenuPrepared();
}
public void setMenu(Menu menu, MenuPresenter.Callback cb) {
pullChildren();
this.mDecorToolbar.setMenu(menu, cb);
}
public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
pullChildren();
this.mDecorToolbar.saveHierarchyState(toolbarStates);
}
public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
pullChildren();
this.mDecorToolbar.restoreHierarchyState(toolbarStates);
}
public void dismissPopups() {
pullChildren();
this.mDecorToolbar.dismissPopupMenus();
}
}