Commit 38881e37 authored by newt@chromium.org's avatar newt@chromium.org

Make InfobarContainer scrollable.

If only this were simple.

BUG=393999
CQ_TRYBOTS=tryserver.chromium.linux:android_aosp,android_clang_dbg,android_dbg

Review URL: https://codereview.chromium.org/487273004

Cr-Commit-Position: refs/heads/master@{#291275}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291275 0039d316-1c4b-4281-b951-d872f2087c98
parent 2a007701
...@@ -13,6 +13,7 @@ import android.os.Build; ...@@ -13,6 +13,7 @@ import android.os.Build;
import android.view.View; import android.view.View;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;
import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApiCompatibilityUtils;
...@@ -45,6 +46,7 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener ...@@ -45,6 +46,7 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener
public static final int ANIMATION_TYPE_BOUNDARY = 3; public static final int ANIMATION_TYPE_BOUNDARY = 3;
private final InfoBarContainer mContainer; private final InfoBarContainer mContainer;
private final LinearLayout mLinearLayout;
private final InfoBar mInfoBar; private final InfoBar mInfoBar;
private final ContentWrapperView mTargetWrapperView; private final ContentWrapperView mTargetWrapperView;
private final AnimatorSet mAnimatorSet; private final AnimatorSet mAnimatorSet;
...@@ -66,12 +68,13 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener ...@@ -66,12 +68,13 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener
public AnimationHelper(InfoBarContainer container, ContentWrapperView target, InfoBar infoBar, public AnimationHelper(InfoBarContainer container, ContentWrapperView target, InfoBar infoBar,
View toShow, int animationType) { View toShow, int animationType) {
mContainer = container; mContainer = container;
mLinearLayout = container.getLinearLayout();
mInfoBar = infoBar; mInfoBar = infoBar;
mTargetWrapperView = target; mTargetWrapperView = target;
mAnimatorSet = new AnimatorSet(); mAnimatorSet = new AnimatorSet();
mAnimationType = animationType; mAnimationType = animationType;
mToShow = toShow; mToShow = toShow;
assert mContainer.indexOfChild(mTargetWrapperView) != -1; assert mLinearLayout.indexOfChild(mTargetWrapperView) != -1;
} }
/** /**
...@@ -124,7 +127,7 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener ...@@ -124,7 +127,7 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener
if (mAnimationStarted) return; if (mAnimationStarted) return;
mAnimationStarted = true; mAnimationStarted = true;
int indexOfWrapperView = mContainer.indexOfChild(mTargetWrapperView); int indexOfWrapperView = mLinearLayout.indexOfChild(mTargetWrapperView);
assert indexOfWrapperView != -1; assert indexOfWrapperView != -1;
ArrayList<Animator> animators = new ArrayList<Animator>(); ArrayList<Animator> animators = new ArrayList<Animator>();
...@@ -145,8 +148,8 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener ...@@ -145,8 +148,8 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener
cumulativeTopEnd = -heightDifference; cumulativeTopEnd = -heightDifference;
} }
for (int i = 0; i < mContainer.getChildCount(); ++i) { for (int i = 0; i < mLinearLayout.getChildCount(); ++i) {
View view = mContainer.getChildAt(i); View view = mLinearLayout.getChildAt(i);
// At this point, the View being transitioned in shouldn't have been added to the // At this point, the View being transitioned in shouldn't have been added to the
// visible container, yet, and shouldn't affect calculations. // visible container, yet, and shouldn't affect calculations.
...@@ -200,10 +203,10 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener ...@@ -200,10 +203,10 @@ public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener
// Lock the InfoBarContainer's size at its largest during the animation to avoid // Lock the InfoBarContainer's size at its largest during the animation to avoid
// clipping issues. // clipping issues.
int oldContainerTop = mContainer.getTop(); int oldContainerTop = mLinearLayout.getTop();
int newContainerTop = mContainer.getBottom() - cumulativeEndHeight; int newContainerTop = mLinearLayout.getBottom() - cumulativeEndHeight;
int biggestContainerTop = Math.min(oldContainerTop, newContainerTop); int biggestContainerTop = Math.min(oldContainerTop, newContainerTop);
mContainer.setTop(biggestContainerTop); mLinearLayout.setTop(biggestContainerTop);
// Set up and run all of the animations. // Set up and run all of the animations.
mAnimatorSet.addListener(new AnimatorListenerAdapter() { mAnimatorSet.addListener(new AnimatorListenerAdapter() {
......
...@@ -83,7 +83,7 @@ public class ContentWrapperView extends FrameLayout { ...@@ -83,7 +83,7 @@ public class ContentWrapperView extends FrameLayout {
* XHDPI devices and above get a double-tall boundary. * XHDPI devices and above get a double-tall boundary.
* @return The height of the boundary. * @return The height of the boundary.
*/ */
private int getBoundaryHeight(Context context) { static int getBoundaryHeight(Context context) {
float density = context.getResources().getDisplayMetrics().density; float density = context.getResources().getDisplayMetrics().density;
return density < 2.0f ? 1 : 2; return density < 2.0f ? 1 : 2;
} }
......
...@@ -7,18 +7,22 @@ package org.chromium.chrome.browser.infobar; ...@@ -7,18 +7,22 @@ package org.chromium.chrome.browser.infobar;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.app.Activity; import android.app.Activity;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.Gravity; import android.view.Gravity;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative; import org.chromium.base.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
import org.chromium.ui.base.DeviceFormFactor;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -32,9 +36,11 @@ import java.util.LinkedList; ...@@ -32,9 +36,11 @@ import java.util.LinkedList;
* When initiated from native code, special code is needed to keep the Java and native infobar in * When initiated from native code, special code is needed to keep the Java and native infobar in
* sync, see NativeInfoBar. * sync, see NativeInfoBar.
*/ */
public class InfoBarContainer extends LinearLayout { public class InfoBarContainer extends ScrollView {
private static final String TAG = "InfoBarContainer"; private static final String TAG = "InfoBarContainer";
private static final long REATTACH_FADE_IN_MS = 250; 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. * A listener for the InfoBar animation.
...@@ -94,10 +100,34 @@ public class InfoBarContainer extends LinearLayout { ...@@ -94,10 +100,34 @@ public class InfoBarContainer extends LinearLayout {
// Parent view that contains us. // Parent view that contains us.
private ViewGroup mParentView; 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, public InfoBarContainer(Activity activity, AutoLoginProcessor autoLoginProcessor,
int tabId, ViewGroup parentView, WebContents webContents) { int tabId, ViewGroup parentView, WebContents webContents) {
super(activity); 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; mAnimationListener = null;
mInfoBarTransitions = new ArrayDeque<InfoBarTransitionInfo>(); mInfoBarTransitions = new ArrayDeque<InfoBarTransitionInfo>();
...@@ -109,13 +139,18 @@ public class InfoBarContainer extends LinearLayout { ...@@ -109,13 +139,18 @@ public class InfoBarContainer extends LinearLayout {
mAnimationSizer = new FrameLayout(activity); mAnimationSizer = new FrameLayout(activity);
mAnimationSizer.setVisibility(INVISIBLE); mAnimationSizer.setVisibility(INVISIBLE);
setGravity(Gravity.BOTTOM);
// Chromium's InfoBarContainer may add an InfoBar immediately during this initialization // Chromium's InfoBarContainer may add an InfoBar immediately during this initialization
// call, so make sure everything in the InfoBarContainer is completely ready beforehand. // call, so make sure everything in the InfoBarContainer is completely ready beforehand.
mNativeInfoBarContainer = nativeInit(webContents, mAutoLoginDelegate); mNativeInfoBarContainer = nativeInit(webContents, mAutoLoginDelegate);
} }
/**
* @return The LinearLayout that holds the infobars (i.e. the ContentWrapperViews).
*/
LinearLayout getLinearLayout() {
return mLinearLayout;
}
public void setAnimationListener(InfoBarAnimationListener listener) { public void setAnimationListener(InfoBarAnimationListener listener) {
mAnimationListener = listener; mAnimationListener = listener;
} }
...@@ -125,33 +160,18 @@ public class InfoBarContainer extends LinearLayout { ...@@ -125,33 +160,18 @@ public class InfoBarContainer extends LinearLayout {
return mAnimationListener; return mAnimationListener;
} }
public boolean areInfoBarsOnTop() {
return false;
}
@Override @Override
public boolean onInterceptTouchEvent(MotionEvent ev) { public boolean onInterceptTouchEvent(MotionEvent ev) {
// Trap any attempts to fiddle with the Views while we're animating. // Trap any attempts to fiddle with the infobars while we're animating.
return mAnimation != null; return super.onInterceptTouchEvent(ev) || mAnimation != null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Consume all motion events so they do not reach the ContentView.
return true;
} }
private void addToParentView() { private void addToParentView() {
if (mParentView != null && mParentView.indexOfChild(this) == -1) { 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() { public void removeFromParentView() {
if (getParent() != null) { if (getParent() != null) {
((ViewGroup) getParent()).removeView(this); ((ViewGroup) getParent()).removeView(this);
...@@ -170,24 +190,6 @@ public class InfoBarContainer extends LinearLayout { ...@@ -170,24 +190,6 @@ public class InfoBarContainer extends LinearLayout {
addToParentView(); addToParentView();
} }
@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 @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
...@@ -321,6 +323,42 @@ public class InfoBarContainer extends LinearLayout { ...@@ -321,6 +323,42 @@ public class InfoBarContainer extends LinearLayout {
} }
} }
super.onLayout(changed, l, t, r, b); 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);
}
} }
/** /**
...@@ -345,7 +383,7 @@ public class InfoBarContainer extends LinearLayout { ...@@ -345,7 +383,7 @@ public class InfoBarContainer extends LinearLayout {
targetView = info.target.getContentWrapper(true); targetView = info.target.getContentWrapper(true);
assert mInfoBars.contains(info.target); assert mInfoBars.contains(info.target);
toShow = targetView.detachCurrentView(); toShow = targetView.detachCurrentView();
addView(targetView, 0, mLinearLayout.addView(targetView, 0,
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else { } else {
targetView = info.target.getContentWrapper(false); targetView = info.target.getContentWrapper(false);
...@@ -380,7 +418,7 @@ public class InfoBarContainer extends LinearLayout { ...@@ -380,7 +418,7 @@ public class InfoBarContainer extends LinearLayout {
public void destroy() { public void destroy() {
mDestroyed = true; mDestroyed = true;
removeAllViews(); mLinearLayout.removeAllViews();
if (mNativeInfoBarContainer != 0) { if (mNativeInfoBarContainer != 0) {
nativeDestroy(mNativeInfoBarContainer); nativeDestroy(mNativeInfoBarContainer);
} }
...@@ -426,7 +464,8 @@ public class InfoBarContainer extends LinearLayout { ...@@ -426,7 +464,8 @@ public class InfoBarContainer extends LinearLayout {
if (parent != null) parent.removeView(toShow); if (parent != null) parent.removeView(toShow);
assert mAnimationSizer.getParent() == null; assert mAnimationSizer.getParent() == null;
mParentView.addView(mAnimationSizer, createLayoutParams()); mParentView.addView(mAnimationSizer, new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mAnimationSizer.addView(toShow, 0, mAnimationSizer.addView(toShow, 0,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mAnimationSizer.requestLayout(); mAnimationSizer.requestLayout();
...@@ -441,19 +480,19 @@ public class InfoBarContainer extends LinearLayout { ...@@ -441,19 +480,19 @@ public class InfoBarContainer extends LinearLayout {
// If the InfoBar was hidden, get rid of its View entirely. // If the InfoBar was hidden, get rid of its View entirely.
if (mAnimation.getAnimationType() == AnimationHelper.ANIMATION_TYPE_HIDE) { 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. // Reset all translations and put everything where they need to be.
for (int i = 0; i < getChildCount(); ++i) { for (int i = 0; i < mLinearLayout.getChildCount(); ++i) {
View view = getChildAt(i); View view = mLinearLayout.getChildAt(i);
view.setTranslationY(0); view.setTranslationY(0);
} }
requestLayout(); requestLayout();
// If there are no infobars shown, there is no need to keep the infobar container in the // If there are no infobars shown, there is no need to keep the infobar container in the
// view hierarchy. // view hierarchy.
if (getChildCount() == 0) { if (mLinearLayout.getChildCount() == 0) {
removeFromParentView(); removeFromParentView();
} }
...@@ -490,6 +529,5 @@ public class InfoBarContainer extends LinearLayout { ...@@ -490,6 +529,5 @@ public class InfoBarContainer extends LinearLayout {
} }
private native long nativeInit(WebContents webContents, AutoLoginDelegate autoLoginDelegate); private native long nativeInit(WebContents webContents, AutoLoginDelegate autoLoginDelegate);
private native void nativeDestroy(long nativeInfoBarContainerAndroid); private native void nativeDestroy(long nativeInfoBarContainerAndroid);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment