Commit 09a67769 authored by newt's avatar newt Committed by Commit bot

Max-width, floating infobars.

This adds a width restriction on infobars. If the webpage is wider than
the infobar max width (600dp), the infobars will "float" in the middle
bottom of the page with shadows on the left, top, and right edges of
each infobar.

BUG=396223

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

Cr-Commit-Position: refs/heads/master@{#370740}
parent fbdb86ee
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <item>
<bitmap <bitmap
android:src="@drawable/infobar_shadow" android:src="@drawable/infobar_shadow_top"
android:gravity="top|fill_horizontal" android:gravity="top|fill_horizontal"
android:tileMode="disabled" /> android:tileMode="disabled" />
</item> </item>
<item <item
android:top="10dp" android:top="@dimen/infobar_shadow_height"
android:drawable="@android:color/white" /> android:drawable="@android:color/white" />
</layer-list> </layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/infobar_bg"
android:minHeight="@dimen/infobar_peeking_height_with_shadow"
android:paddingTop="@dimen/infobar_shadow_height" />
...@@ -68,12 +68,20 @@ ...@@ -68,12 +68,20 @@
<dimen name="infobar_big_icon_message_size">18sp</dimen> <dimen name="infobar_big_icon_message_size">18sp</dimen>
<!-- Text size of descriptive controls on an infobar. --> <!-- Text size of descriptive controls on an infobar. -->
<dimen name="infobar_descriptive_text_size">14sp</dimen> <dimen name="infobar_descriptive_text_size">14sp</dimen>
<!-- Minimum width of an infobar. -->
<dimen name="infobar_min_width">220dp</dimen>
<!-- Padding surrounding the infobar. --> <!-- Padding surrounding the infobar. -->
<dimen name="infobar_padding">16dp</dimen> <dimen name="infobar_padding">16dp</dimen>
<!-- Margin between stacked buttons in an infobar. --> <!-- Margin between stacked buttons in an infobar. -->
<dimen name="infobar_margin_between_stacked_buttons">24dp</dimen> <dimen name="infobar_margin_between_stacked_buttons">24dp</dimen>
<!-- Minimum width of an infobar. -->
<dimen name="infobar_min_width">220dp</dimen>
<!-- Maximum width of an infobar. -->
<dimen name="infobar_max_width">600dp</dimen>
<!-- Width of side shadows on floating infobars. -->
<dimen name="infobar_shadow_width">8dp</dimen>
<!-- Height of top shadow on each infobar. -->
<dimen name="infobar_shadow_height">8dp</dimen>
<!-- Distance that a back infobar peeks out above the front infobar. -->
<dimen name="infobar_peeking_height">10dp</dimen>
<!-- Vertical margin applied between groups of controls. --> <!-- Vertical margin applied between groups of controls. -->
<dimen name="infobar_margin_above_control_groups">24dp</dimen> <dimen name="infobar_margin_above_control_groups">24dp</dimen>
...@@ -90,10 +98,6 @@ ...@@ -90,10 +98,6 @@
<dimen name="infobar_big_icon_size">48dp</dimen> <dimen name="infobar_big_icon_size">48dp</dimen>
<dimen name="infobar_big_icon_margin">16dp</dimen> <dimen name="infobar_big_icon_margin">16dp</dimen>
<dimen name="infobar_shadow_height">10dp</dimen>
<dimen name="infobar_peeking_height">10dp</dimen>
<dimen name="infobar_peeking_height_with_shadow">20dp</dimen>
<!-- Contextual search dimensions --> <!-- Contextual search dimensions -->
<dimen name="contextual_search_bar_height">56dp</dimen> <dimen name="contextual_search_bar_height">56dp</dimen>
<dimen name="contextual_search_text_size">18sp</dimen> <dimen name="contextual_search_text_size">18sp</dimen>
......
...@@ -115,7 +115,8 @@ public class InfoBarContainer extends SwipableOverlayView { ...@@ -115,7 +115,8 @@ public class InfoBarContainer extends SwipableOverlayView {
mParentView = parentView; mParentView = parentView;
mLayout = new InfoBarContainerLayout(context); mLayout = new InfoBarContainerLayout(context);
addView(mLayout); addView(mLayout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL));
// 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.
......
...@@ -8,12 +8,13 @@ import android.animation.Animator; ...@@ -8,12 +8,13 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import org.chromium.chrome.R; import org.chromium.chrome.R;
...@@ -58,10 +59,7 @@ import java.util.Collection; ...@@ -58,10 +59,7 @@ import java.util.Collection;
* Or can we just call setEnabled() false on the infobar wrapper? Will this cause the buttons * Or can we just call setEnabled() false on the infobar wrapper? Will this cause the buttons
* visual state to change (i.e. to turn gray)? * visual state to change (i.e. to turn gray)?
* *
* TODO(newt): finalize animation timings. * TODO(newt): finalize animation timings and interpolators.
*
* TODO: investigate major lag on Nexus 7 runing KK when tapping e.g. "French" on the translation
* infobar to trigger the Change Languages panel.
*/ */
class InfoBarContainerLayout extends FrameLayout { class InfoBarContainerLayout extends FrameLayout {
...@@ -99,9 +97,9 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -99,9 +97,9 @@ class InfoBarContainerLayout extends FrameLayout {
*/ */
InfoBarContainerLayout(Context context) { InfoBarContainerLayout(Context context) {
super(context); super(context);
setChildrenDrawingOrderEnabled(true); Resources res = context.getResources();
mBackInfobarHeight = context.getResources().getDimensionPixelSize( mBackInfobarHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height);
R.dimen.infobar_peeking_height); mFloatingBehavior = new FloatingBehavior(this);
} }
/** /**
...@@ -199,6 +197,22 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -199,6 +197,22 @@ class InfoBarContainerLayout extends FrameLayout {
mAnimator.start(); mAnimator.start();
} }
/**
* Returns an animator that animates an InfoBarWrapper's y-translation from its current
* value to endValue and updates the side shadow positions on each frame.
*/
ValueAnimator createTranslationYAnimator(final InfoBarWrapper wrapper, float endValue) {
ValueAnimator animator = ValueAnimator.ofFloat(wrapper.getTranslationY(), endValue);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
wrapper.setTranslationY((float) animation.getAnimatedValue());
mFloatingBehavior.updateShadowPosition();
}
});
return animator;
}
/** /**
* Called before the animation begins. This is the time to add views to the hierarchy and * Called before the animation begins. This is the time to add views to the hierarchy and
* adjust layout parameters. * adjust layout parameters.
...@@ -229,32 +243,30 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -229,32 +243,30 @@ class InfoBarContainerLayout extends FrameLayout {
* content fades in. * content fades in.
*/ */
private class FrontInfoBarAppearingAnimation extends InfoBarAnimation { private class FrontInfoBarAppearingAnimation extends InfoBarAnimation {
private ViewGroup mFrontView; private InfoBarWrapper mFrontWrapper;
private View mFrontInnerView; private View mFrontContents;
FrontInfoBarAppearingAnimation(View frontInnerView) { FrontInfoBarAppearingAnimation(View frontContents) {
mFrontInnerView = frontInnerView; mFrontContents = frontContents;
} }
@Override @Override
void prepareAnimation() { void prepareAnimation() {
mFrontView = (ViewGroup) LayoutInflater.from(getContext()).inflate( mFrontWrapper = new InfoBarWrapper(getContext());
R.layout.infobar_wrapper, InfoBarContainerLayout.this, false); mFrontWrapper.addView(mFrontContents);
addInnerView(mFrontView, mFrontInnerView); addWrapper(mFrontWrapper);
addView(mFrontView);
updateLayoutParams();
} }
@Override @Override
Animator createAnimator() { Animator createAnimator() {
mFrontView.setTranslationY(mFrontView.getHeight()); mFrontWrapper.setTranslationY(mFrontWrapper.getHeight());
mFrontInnerView.setAlpha(0f); mFrontContents.setAlpha(0f);
AnimatorSet animator = new AnimatorSet(); AnimatorSet animator = new AnimatorSet();
animator.playSequentially( animator.playSequentially(
ObjectAnimator.ofFloat(mFrontView, View.TRANSLATION_Y, 0f) createTranslationYAnimator(mFrontWrapper, 0f)
.setDuration(DURATION_SLIDE_UP_MS), .setDuration(DURATION_SLIDE_UP_MS),
ObjectAnimator.ofFloat(mFrontInnerView, View.ALPHA, 1f) ObjectAnimator.ofFloat(mFrontContents, View.ALPHA, 1f)
.setDuration(DURATION_FADE_MS)); .setDuration(DURATION_FADE_MS));
return animator; return animator;
} }
...@@ -275,20 +287,18 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -275,20 +287,18 @@ class InfoBarContainerLayout extends FrameLayout {
* its top edge peeks out just a bit. * its top edge peeks out just a bit.
*/ */
private class BackInfoBarAppearingAnimation extends InfoBarAnimation { private class BackInfoBarAppearingAnimation extends InfoBarAnimation {
private View mAppearingView; private InfoBarWrapper mAppearingWrapper;
@Override @Override
void prepareAnimation() { void prepareAnimation() {
mAppearingView = LayoutInflater.from(getContext()).inflate(R.layout.infobar_wrapper, mAppearingWrapper = new InfoBarWrapper(getContext());
InfoBarContainerLayout.this, false); addWrapper(mAppearingWrapper);
addView(mAppearingView);
updateLayoutParams();
} }
@Override @Override
Animator createAnimator() { Animator createAnimator() {
mAppearingView.setTranslationY(mAppearingView.getHeight()); mAppearingWrapper.setTranslationY(mAppearingWrapper.getHeight());
return ObjectAnimator.ofFloat(mAppearingView, View.TRANSLATION_Y, 0f) return createTranslationYAnimator(mAppearingWrapper, 0f)
.setDuration(DURATION_SLIDE_UP_MS); .setDuration(DURATION_SLIDE_UP_MS);
} }
...@@ -304,46 +314,46 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -304,46 +314,46 @@ class InfoBarContainerLayout extends FrameLayout {
* new front infobar, and then the new front infobar's contents will fade in. * new front infobar, and then the new front infobar's contents will fade in.
*/ */
private class FrontInfoBarDisappearingAndRevealingAnimation extends InfoBarAnimation { private class FrontInfoBarDisappearingAndRevealingAnimation extends InfoBarAnimation {
private ViewGroup mOldFrontView; private InfoBarWrapper mOldFrontWrapper;
private ViewGroup mNewFrontView; private InfoBarWrapper mNewFrontWrapper;
private View mNewFrontInnerView; private View mNewFrontContents;
FrontInfoBarDisappearingAndRevealingAnimation(View newFrontInnerView) { FrontInfoBarDisappearingAndRevealingAnimation(View newFrontContents) {
mNewFrontInnerView = newFrontInnerView; mNewFrontContents = newFrontContents;
} }
@Override @Override
void prepareAnimation() { void prepareAnimation() {
mOldFrontView = (ViewGroup) getChildAt(0); mOldFrontWrapper = mInfoBarWrappers.get(0);
mNewFrontView = (ViewGroup) getChildAt(1); mNewFrontWrapper = mInfoBarWrappers.get(1);
addInnerView(mNewFrontView, mNewFrontInnerView); mNewFrontWrapper.addView(mNewFrontContents);
} }
@Override @Override
Animator createAnimator() { Animator createAnimator() {
// The amount by which mNewFrontView will grow (a negative value indicates shrinking). // The amount by which mNewFrontWrapper will grow (negative value indicates shrinking).
int deltaHeight = (mNewFrontView.getHeight() - mBackInfobarHeight) int deltaHeight = (mNewFrontWrapper.getHeight() - mBackInfobarHeight)
- mOldFrontView.getHeight(); - mOldFrontWrapper.getHeight();
int startTranslationY = Math.max(deltaHeight, 0); int startTranslationY = Math.max(deltaHeight, 0);
int endTranslationY = Math.max(-deltaHeight, 0); int endTranslationY = Math.max(-deltaHeight, 0);
// Slide the front infobar down and away. // Slide the front infobar down and away.
AnimatorSet animator = new AnimatorSet(); AnimatorSet animator = new AnimatorSet();
mOldFrontView.setTranslationY(startTranslationY); mOldFrontWrapper.setTranslationY(startTranslationY);
animator.play(ObjectAnimator.ofFloat(mOldFrontView, View.TRANSLATION_Y, animator.play(createTranslationYAnimator(mOldFrontWrapper,
startTranslationY + mOldFrontView.getHeight()) startTranslationY + mOldFrontWrapper.getHeight())
.setDuration(DURATION_SLIDE_UP_MS)); .setDuration(DURATION_SLIDE_UP_MS));
// Slide the other infobars to their new positions. // Slide the other infobars to their new positions.
// Note: animator.play() causes these animations to run simultaneously. // Note: animator.play() causes these animations to run simultaneously.
for (int i = 1; i < getChildCount(); i++) { for (int i = 1; i < mInfoBarWrappers.size(); i++) {
getChildAt(i).setTranslationY(startTranslationY); mInfoBarWrappers.get(i).setTranslationY(startTranslationY);
animator.play(ObjectAnimator.ofFloat(getChildAt(i), View.TRANSLATION_Y, animator.play(createTranslationYAnimator(mInfoBarWrappers.get(i),
endTranslationY).setDuration(DURATION_SLIDE_UP_MS)); endTranslationY).setDuration(DURATION_SLIDE_UP_MS));
} }
mNewFrontInnerView.setAlpha(0f); mNewFrontContents.setAlpha(0f);
animator.play(ObjectAnimator.ofFloat(mNewFrontInnerView, View.ALPHA, 1f) animator.play(ObjectAnimator.ofFloat(mNewFrontContents, View.ALPHA, 1f)
.setDuration(DURATION_FADE_MS)).after(DURATION_SLIDE_UP_MS); .setDuration(DURATION_FADE_MS)).after(DURATION_SLIDE_UP_MS);
return animator; return animator;
...@@ -351,12 +361,11 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -351,12 +361,11 @@ class InfoBarContainerLayout extends FrameLayout {
@Override @Override
void onAnimationEnd() { void onAnimationEnd() {
mOldFrontView.removeAllViews(); mOldFrontWrapper.removeAllViews();
removeView(mOldFrontView); removeWrapper(mOldFrontWrapper);
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < mInfoBarWrappers.size(); i++) {
getChildAt(i).setTranslationY(0); mInfoBarWrappers.get(i).setTranslationY(0);
} }
updateLayoutParams();
announceForAccessibility(mFrontItem.getAccessibilityText()); announceForAccessibility(mFrontItem.getAccessibilityText());
} }
...@@ -371,25 +380,24 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -371,25 +380,24 @@ class InfoBarContainerLayout extends FrameLayout {
* The infobar simply slides down out of the container. * The infobar simply slides down out of the container.
*/ */
private class InfoBarDisappearingAnimation extends InfoBarAnimation { private class InfoBarDisappearingAnimation extends InfoBarAnimation {
private ViewGroup mDisappearingView; private InfoBarWrapper mDisappearingWrapper;
@Override @Override
void prepareAnimation() { void prepareAnimation() {
mDisappearingView = (ViewGroup) getChildAt(getChildCount() - 1); mDisappearingWrapper = mInfoBarWrappers.get(mInfoBarWrappers.size() - 1);
} }
@Override @Override
Animator createAnimator() { Animator createAnimator() {
return ObjectAnimator.ofFloat(mDisappearingView, View.TRANSLATION_Y, return createTranslationYAnimator(mDisappearingWrapper,
mDisappearingView.getHeight()) mDisappearingWrapper.getHeight())
.setDuration(DURATION_SLIDE_DOWN_MS); .setDuration(DURATION_SLIDE_DOWN_MS);
} }
@Override @Override
void onAnimationEnd() { void onAnimationEnd() {
mDisappearingView.removeAllViews(); mDisappearingWrapper.removeAllViews();
removeView(mDisappearingView); removeWrapper(mDisappearingWrapper);
updateLayoutParams();
} }
@Override @Override
...@@ -403,41 +411,41 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -403,41 +411,41 @@ class InfoBarContainerLayout extends FrameLayout {
* then the infobar resizes to fit the new contents, then the new contents fade in. * then the infobar resizes to fit the new contents, then the new contents fade in.
*/ */
private class FrontInfoBarSwapContentsAnimation extends InfoBarAnimation { private class FrontInfoBarSwapContentsAnimation extends InfoBarAnimation {
private ViewGroup mFrontView; private InfoBarWrapper mFrontWrapper;
private View mOldInnerView; private View mOldContents;
private View mNewInnerView; private View mNewContents;
FrontInfoBarSwapContentsAnimation(View newInnerView) { FrontInfoBarSwapContentsAnimation(View newContents) {
mNewInnerView = newInnerView; mNewContents = newContents;
} }
@Override @Override
void prepareAnimation() { void prepareAnimation() {
mFrontView = (ViewGroup) getChildAt(0); mFrontWrapper = mInfoBarWrappers.get(0);
mOldInnerView = mFrontView.getChildAt(0); mOldContents = mFrontWrapper.getChildAt(0);
addInnerView(mFrontView, mNewInnerView); mFrontWrapper.addView(mNewContents);
} }
@Override @Override
Animator createAnimator() { Animator createAnimator() {
int deltaHeight = mNewInnerView.getHeight() - mOldInnerView.getHeight(); int deltaHeight = mNewContents.getHeight() - mOldContents.getHeight();
InfoBarContainerLayout.this.setTranslationY(Math.max(0, deltaHeight)); InfoBarContainerLayout.this.setTranslationY(Math.max(0, deltaHeight));
mNewInnerView.setAlpha(0f); mNewContents.setAlpha(0f);
AnimatorSet animator = new AnimatorSet(); AnimatorSet animator = new AnimatorSet();
animator.playSequentially( animator.playSequentially(
ObjectAnimator.ofFloat(mOldInnerView, View.ALPHA, 0f) ObjectAnimator.ofFloat(mOldContents, View.ALPHA, 0f)
.setDuration(DURATION_FADE_OUT_MS), .setDuration(DURATION_FADE_OUT_MS),
ObjectAnimator.ofFloat(InfoBarContainerLayout.this, View.TRANSLATION_Y, ObjectAnimator.ofFloat(InfoBarContainerLayout.this, View.TRANSLATION_Y,
Math.max(0, -deltaHeight)).setDuration(DURATION_SLIDE_UP_MS), Math.max(0, -deltaHeight)).setDuration(DURATION_SLIDE_UP_MS),
ObjectAnimator.ofFloat(mNewInnerView, View.ALPHA, 1f) ObjectAnimator.ofFloat(mNewContents, View.ALPHA, 1f)
.setDuration(DURATION_FADE_OUT_MS)); .setDuration(DURATION_FADE_OUT_MS));
return animator; return animator;
} }
@Override @Override
void onAnimationEnd() { void onAnimationEnd() {
mFrontView.removeViewAt(0); mFrontWrapper.removeViewAt(0);
InfoBarContainerLayout.this.setTranslationY(0f); InfoBarContainerLayout.this.setTranslationY(0f);
mFrontItem.setControlsEnabled(true); mFrontItem.setControlsEnabled(true);
announceForAccessibility(mFrontItem.getAccessibilityText()); announceForAccessibility(mFrontItem.getAccessibilityText());
...@@ -449,6 +457,130 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -449,6 +457,130 @@ class InfoBarContainerLayout extends FrameLayout {
} }
} }
/**
* Controls whether infobars fill the full available width, or whether they "float" in the
* middle of the available space. The latter case happens if the available space is wider than
* the max width allowed for infobars.
*
* Also handles the shadows on the sides of the infobars in floating mode. The side shadows are
* separate views -- rather than being part of each InfoBarWrapper -- to avoid a double-shadow
* effect, which would happen during animations when two InfoBarWrappers overlap each other.
*/
private static class FloatingBehavior {
/** The InfoBarContainerLayout. */
private FrameLayout mLayout;
/**
* The max width of the infobars. If the available space is wider than this, the infobars
* will switch to floating mode.
*/
private final int mMaxWidth;
/** The width of the left and right shadows. */
private final int mShadowWidth;
/** Whether the layout is currently floating. */
private boolean mIsFloating;
/** The shadows that appear on the sides of the infobars in floating mode. */
private View mLeftShadowView;
private View mRightShadowView;
FloatingBehavior(FrameLayout layout) {
mLayout = layout;
Resources res = mLayout.getContext().getResources();
mMaxWidth = res.getDimensionPixelSize(R.dimen.infobar_max_width);
mShadowWidth = res.getDimensionPixelSize(R.dimen.infobar_shadow_width);
}
/**
* This should be called in onMeasure() before super.onMeasure(). The return value is a new
* widthMeasureSpec that should be passed to super.onMeasure().
*/
int beforeOnMeasure(int widthMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
boolean isFloating = width > mMaxWidth;
if (isFloating != mIsFloating) {
mIsFloating = isFloating;
onIsFloatingChanged();
}
if (isFloating) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
width = Math.min(width, mMaxWidth + 2 * mShadowWidth);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, mode);
}
return widthMeasureSpec;
}
/**
* This should be called in onMeasure() after super.onMeasure().
*/
void afterOnMeasure(int measuredHeight) {
if (!mIsFloating) return;
// Measure side shadows to match the parent view's height.
int widthSpec = MeasureSpec.makeMeasureSpec(mShadowWidth, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY);
mLeftShadowView.measure(widthSpec, heightSpec);
mRightShadowView.measure(widthSpec, heightSpec);
}
/**
* This should be called whenever the Y-position of an infobar changes.
*/
void updateShadowPosition() {
if (!mIsFloating) return;
float minY = mLayout.getHeight();
int childCount = mLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mLayout.getChildAt(i);
if (child != mLeftShadowView && child != mRightShadowView) {
minY = Math.min(minY, child.getY());
}
}
mLeftShadowView.setY(minY);
mRightShadowView.setY(minY);
}
private void onIsFloatingChanged() {
if (mIsFloating) {
initShadowViews();
mLayout.setPadding(mShadowWidth, 0, mShadowWidth, 0);
mLayout.setClipToPadding(false);
mLayout.addView(mLeftShadowView);
mLayout.addView(mRightShadowView);
} else {
mLayout.setPadding(0, 0, 0, 0);
mLayout.removeView(mLeftShadowView);
mLayout.removeView(mRightShadowView);
}
}
@SuppressLint("RtlHardcoded")
private void initShadowViews() {
if (mLeftShadowView != null) return;
mLeftShadowView = new View(mLayout.getContext());
mLeftShadowView.setBackgroundResource(R.drawable.infobar_shadow_left);
LayoutParams leftLp = new FrameLayout.LayoutParams(0, 0, Gravity.LEFT);
leftLp.leftMargin = -mShadowWidth;
mLeftShadowView.setLayoutParams(leftLp);
mRightShadowView = new View(mLayout.getContext());
mRightShadowView.setBackgroundResource(R.drawable.infobar_shadow_left);
LayoutParams rightLp = new FrameLayout.LayoutParams(0, 0, Gravity.RIGHT);
rightLp.rightMargin = -mShadowWidth;
mRightShadowView.setScaleX(-1f);
mRightShadowView.setLayoutParams(rightLp);
}
}
/**
* The height of back infobars, i.e. the distance between the top of the front infobar and the
* top of the next infobar back.
*/
private final int mBackInfobarHeight;
/** /**
* All the Items, in front to back order. * All the Items, in front to back order.
* This list is updated immediately when addInfoBar(), removeInfoBar(), and swapInfoBar() are * This list is updated immediately when addInfoBar(), removeInfoBar(), and swapInfoBar() are
...@@ -462,16 +594,17 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -462,16 +594,17 @@ class InfoBarContainerLayout extends FrameLayout {
*/ */
private Item mFrontItem; private Item mFrontItem;
/**
* The list of InfoBarWrapper views that are currently visible.
*/
private final ArrayList<InfoBarWrapper> mInfoBarWrappers = new ArrayList<>();
/** The current animation, or null if no animation is happening currently. */ /** The current animation, or null if no animation is happening currently. */
private InfoBarAnimation mAnimation; private InfoBarAnimation mAnimation;
private InfoBarAnimationListener mAnimationListener; private InfoBarAnimationListener mAnimationListener;
/** private FloatingBehavior mFloatingBehavior;
* The height of back infobars, i.e. the distance between the top of the front infobar and the
* top of the next infobar back.
*/
private int mBackInfobarHeight;
/** /**
* Determines whether any animations need to run in order to make the visible views match the * Determines whether any animations need to run in order to make the visible views match the
...@@ -485,7 +618,7 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -485,7 +618,7 @@ class InfoBarContainerLayout extends FrameLayout {
// removals happen before additions or swaps, and changes are made to back infobars before // removals happen before additions or swaps, and changes are made to back infobars before
// front infobars. // front infobars.
int childCount = getChildCount(); int childCount = mInfoBarWrappers.size();
int desiredChildCount = Math.min(mItems.size(), MAX_STACK_DEPTH); int desiredChildCount = Math.min(mItems.size(), MAX_STACK_DEPTH);
boolean shouldRemoveFrontView = mFrontItem != null && !mItems.contains(mFrontItem); boolean shouldRemoveFrontView = mFrontItem != null && !mItems.contains(mFrontItem);
...@@ -513,8 +646,8 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -513,8 +646,8 @@ class InfoBarContainerLayout extends FrameLayout {
// Third, run swap animation on front infobar if needed. // Third, run swap animation on front infobar if needed.
if (mFrontItem != null && mItems.contains(mFrontItem)) { if (mFrontItem != null && mItems.contains(mFrontItem)) {
View frontInnerView = ((ViewGroup) getChildAt(0)).getChildAt(0); View frontContents = mInfoBarWrappers.get(0).getChildAt(0);
if (frontInnerView != mFrontItem.getView()) { if (frontContents != mFrontItem.getView()) {
runAnimation(new FrontInfoBarSwapContentsAnimation(mFrontItem.getView())); runAnimation(new FrontInfoBarSwapContentsAnimation(mFrontItem.getView()));
return; return;
} }
...@@ -543,28 +676,40 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -543,28 +676,40 @@ class InfoBarContainerLayout extends FrameLayout {
} }
} }
/** private void addWrapper(InfoBarWrapper wrapper) {
* Adds an infobar view to a wrapper view, with suitable LayoutParams. addView(wrapper, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
*/ mInfoBarWrappers.add(wrapper);
private void addInnerView(ViewGroup wrapperView, View innerView) { updateLayoutParams();
wrapperView.addView(innerView, new LayoutParams( }
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.TOP));
private void removeWrapper(InfoBarWrapper wrapper) {
removeView(wrapper);
mInfoBarWrappers.remove(wrapper);
updateLayoutParams();
} }
private void updateLayoutParams() { private void updateLayoutParams() {
// Stagger the top margins so the back infobars peek out a bit. // Stagger the top margins so the back infobars peek out a bit.
int childCount = getChildCount(); int childCount = mInfoBarWrappers.size();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
View child = getChildAt(i); View child = mInfoBarWrappers.get(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams(); LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.topMargin = (childCount - 1 - i) * mBackInfobarHeight; lp.topMargin = (childCount - 1 - i) * mBackInfobarHeight;
child.setLayoutParams(lp); child.setLayoutParams(lp);
} }
} }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = mFloatingBehavior.beforeOnMeasure(widthMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mFloatingBehavior.afterOnMeasure(getMeasuredHeight());
}
@Override @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom); super.onLayout(changed, left, top, right, bottom);
mFloatingBehavior.updateShadowPosition();
// Animations start after a layout has completed, at which point all views are guaranteed // Animations start after a layout has completed, at which point all views are guaranteed
// to have valid sizes and positions. // to have valid sizes and positions.
...@@ -573,13 +718,6 @@ class InfoBarContainerLayout extends FrameLayout { ...@@ -573,13 +718,6 @@ class InfoBarContainerLayout extends FrameLayout {
} }
} }
@Override
protected int getChildDrawingOrder(int childCount, int i) {
// Draw children from last to first. This allows us to order the children from front to
// back, which simplifies the logic elsewhere in this class.
return childCount - i - 1;
}
@Override @Override
public boolean onInterceptTouchEvent(MotionEvent ev) { public boolean onInterceptTouchEvent(MotionEvent ev) {
// Trap any attempts to fiddle with the infobars while we're animating. // Trap any attempts to fiddle with the infobars while we're animating.
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.infobar;
import android.content.Context;
import android.content.res.Resources;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import org.chromium.chrome.R;
/**
* Layout that holds an infobar's contents and provides a background color and a top shadow.
*/
class InfoBarWrapper extends FrameLayout {
/**
* Constructor for inflating from Java.
*/
InfoBarWrapper(Context context) {
super(context);
Resources res = context.getResources();
int peekingHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height);
int shadowHeight = res.getDimensionPixelSize(R.dimen.infobar_shadow_height);
setMinimumHeight(peekingHeight + shadowHeight);
// setBackgroundResource() changes the padding, so call setPadding() second.
setBackgroundResource(R.drawable.infobar_wrapper_bg);
setPadding(0, shadowHeight, 0, 0);
}
@Override
public void onViewAdded(View child) {
child.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
Gravity.TOP));
}
}
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