import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.ScrollView;
import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative;
+import org.chromium.chrome.R;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils;
+import org.chromium.ui.base.DeviceFormFactor;
import java.util.ArrayDeque;
import java.util.ArrayList;
* When initiated from native code, special code is needed to keep the Java and native infobar in
* sync, see NativeInfoBar.
*/
-public class InfoBarContainer extends LinearLayout {
+public class InfoBarContainer extends ScrollView {
private static final String TAG = "InfoBarContainer";
private static final long REATTACH_FADE_IN_MS = 250;
+ private static final int TAB_STRIP_AND_TOOLBAR_HEIGHT_PHONE_DP = 56;
+ private static final int TAB_STRIP_AND_TOOLBAR_HEIGHT_TABLET_DP = 96;
/**
* A listener for the InfoBar animation.
// Parent view that contains us.
private ViewGroup mParentView;
+ // The LinearLayout that holds the infobars. This is the only child of the InfoBarContainer.
+ private LinearLayout mLinearLayout;
+
+ // These values are used in onLayout() to keep the infobars fixed to the bottom of the screen
+ // when infobars are added or removed.
+ private int mHeight;
+ private int mInnerHeight;
+ private int mDistanceFromBottom;
+
+ private Paint mTopBorderPaint;
+
public InfoBarContainer(Activity activity, AutoLoginProcessor autoLoginProcessor,
int tabId, ViewGroup parentView, WebContents webContents) {
super(activity);
- setOrientation(LinearLayout.VERTICAL);
+
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.BOTTOM);
+ int topMarginDp = DeviceFormFactor.isTablet(activity)
+ ? TAB_STRIP_AND_TOOLBAR_HEIGHT_TABLET_DP
+ : TAB_STRIP_AND_TOOLBAR_HEIGHT_PHONE_DP;
+ lp.topMargin = Math.round(topMarginDp * getResources().getDisplayMetrics().density);
+ setLayoutParams(lp);
+
+ mLinearLayout = new LinearLayout(activity);
+ mLinearLayout.setOrientation(LinearLayout.VERTICAL);
+ addView(mLinearLayout,
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+
mAnimationListener = null;
mInfoBarTransitions = new ArrayDeque<InfoBarTransitionInfo>();
mAnimationSizer = new FrameLayout(activity);
mAnimationSizer.setVisibility(INVISIBLE);
- setGravity(Gravity.BOTTOM);
-
// Chromium's InfoBarContainer may add an InfoBar immediately during this initialization
// call, so make sure everything in the InfoBarContainer is completely ready beforehand.
mNativeInfoBarContainer = nativeInit(webContents, mAutoLoginDelegate);
}
+ /**
+ * @return The LinearLayout that holds the infobars (i.e. the ContentWrapperViews).
+ */
+ LinearLayout getLinearLayout() {
+ return mLinearLayout;
+ }
+
public void setAnimationListener(InfoBarAnimationListener listener) {
mAnimationListener = listener;
}
return mAnimationListener;
}
- public boolean areInfoBarsOnTop() {
- return false;
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Trap any attempts to fiddle with the Views while we're animating.
- return mAnimation != null;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // Consume all motion events so they do not reach the ContentView.
- return true;
+ // Trap any attempts to fiddle with the infobars while we're animating.
+ return super.onInterceptTouchEvent(ev) || mAnimation != null;
}
private void addToParentView() {
if (mParentView != null && mParentView.indexOfChild(this) == -1) {
- mParentView.addView(this, createLayoutParams());
+ mParentView.addView(this);
}
}
- private FrameLayout.LayoutParams createLayoutParams() {
- return new FrameLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.BOTTOM);
- }
-
public void removeFromParentView() {
if (getParent() != null) {
((ViewGroup) getParent()).removeView(this);
}
@Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (mAnimation == null || child != mAnimation.getTarget()) {
- return super.drawChild(canvas, child, drawingTime);
- }
- // When infobars are on top, the new infobar Z-order is greater than the previous infobar,
- // which means it shows on top during the animation. We cannot change the Z-order in the
- // linear layout, it is driven by the insertion index.
- // So we simply clip the children to their bounds to make sure the new infobar does not
- // paint over.
- boolean retVal;
- canvas.save();
- canvas.clipRect(mAnimation.getTarget().getClippingRect());
- retVal = super.drawChild(canvas, child, drawingTime);
- canvas.restore();
- return retVal;
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ObjectAnimator.ofFloat(this, "alpha", 0.f, 1.f).setDuration(REATTACH_FADE_IN_MS).start();
}
}
super.onLayout(changed, l, t, r, b);
+
+ // Keep the infobars fixed to the bottom of the screen when infobars are added or removed.
+ // Otherwise, infobars jump around when appearing or disappearing on small devices.
+ int newHeight = getHeight();
+ int newInnerHeight = mLinearLayout.getHeight();
+ if (mInnerHeight != newInnerHeight) {
+ int newScrollY = newInnerHeight - newHeight - mDistanceFromBottom;
+ scrollTo(0, newScrollY);
+ }
+ mHeight = newHeight;
+ mInnerHeight = newInnerHeight;
+ mDistanceFromBottom = mInnerHeight - mHeight - getScrollY();
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ mDistanceFromBottom = mInnerHeight - mHeight - getScrollY();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ // If the infobars overflow the ScrollView, draw a border at the top of the ScrollView.
+ // This prevents the topmost infobar from blending into the page when fullscreen mode
+ // is active.
+ if (getScrollY() != 0) {
+ if (mTopBorderPaint == null) {
+ mTopBorderPaint = new Paint();
+ mTopBorderPaint.setColor(
+ getResources().getColor(R.color.infobar_background_separator));
+ }
+ int height = ContentWrapperView.getBoundaryHeight(getContext());
+ canvas.drawRect(0, getScrollY(), getWidth(), getScrollY() + height, mTopBorderPaint);
+ }
}
/**
targetView = info.target.getContentWrapper(true);
assert mInfoBars.contains(info.target);
toShow = targetView.detachCurrentView();
- addView(targetView, 0,
+ mLinearLayout.addView(targetView, 0,
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else {
targetView = info.target.getContentWrapper(false);
public void destroy() {
mDestroyed = true;
- removeAllViews();
+ mLinearLayout.removeAllViews();
if (mNativeInfoBarContainer != 0) {
nativeDestroy(mNativeInfoBarContainer);
}
if (parent != null) parent.removeView(toShow);
assert mAnimationSizer.getParent() == null;
- mParentView.addView(mAnimationSizer, createLayoutParams());
+ mParentView.addView(mAnimationSizer, new FrameLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mAnimationSizer.addView(toShow, 0,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mAnimationSizer.requestLayout();
// If the InfoBar was hidden, get rid of its View entirely.
if (mAnimation.getAnimationType() == AnimationHelper.ANIMATION_TYPE_HIDE) {
- removeView(mAnimation.getTarget());
+ mLinearLayout.removeView(mAnimation.getTarget());
}
// Reset all translations and put everything where they need to be.
- for (int i = 0; i < getChildCount(); ++i) {
- View view = getChildAt(i);
+ for (int i = 0; i < mLinearLayout.getChildCount(); ++i) {
+ View view = mLinearLayout.getChildAt(i);
view.setTranslationY(0);
}
requestLayout();
// If there are no infobars shown, there is no need to keep the infobar container in the
// view hierarchy.
- if (getChildCount() == 0) {
+ if (mLinearLayout.getChildCount() == 0) {
removeFromParentView();
}
}
private native long nativeInit(WebContents webContents, AutoLoginDelegate autoLoginDelegate);
-
private native void nativeDestroy(long nativeInfoBarContainerAndroid);
}