| Index: chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java
|
| index 32c2070944b3f71bdfb4e714c0c5a7de76db0fe6..3a302520a668e9696fb1e467158bd5eaa828f82b 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java
|
| @@ -8,10 +8,10 @@ import android.animation.ObjectAnimator;
|
| import android.content.Context;
|
| import android.content.res.Configuration;
|
| import android.content.res.Resources;
|
| -import android.graphics.Point;
|
| import android.graphics.Rect;
|
| import android.util.AttributeSet;
|
| import android.view.LayoutInflater;
|
| +import android.view.MotionEvent;
|
| import android.view.View;
|
| import android.view.ViewGroup;
|
| import android.widget.Button;
|
| @@ -25,9 +25,29 @@ import org.chromium.ui.base.LocalizationUtils;
|
|
|
| /**
|
| * Lays out a banner for showing info about an app on the Play Store.
|
| - * Rather than utilizing the Android RatingBar, which would require some nasty styling, a custom
|
| - * View is used to paint a Drawable showing all the stars. Showing different ratings is done by
|
| - * adjusting the clipping rectangle width.
|
| + * The banner mimics the appearance of a Google Now card using a background Drawable with a shadow.
|
| + *
|
| + * PADDING CALCULATIONS
|
| + * The banner has three different types of padding that need to be accounted for:
|
| + * 1) The background Drawable of the banner looks like card with a drop shadow. The Drawable
|
| + * defines a padding around the card that solely encompasses the space occupied by the drop
|
| + * shadow.
|
| + * 2) The card itself needs to have padding so that the widgets don't abut the borders of the card.
|
| + * This is defined as mPaddingCard, and is equally applied to all four sides.
|
| + * 3) Controls other than the icon are further constrained by mPaddingControls, which applies only
|
| + * to the bottom and end margins.
|
| + * See {@link #AppBannerView.onMeasure(int, int)} for details.
|
| + *
|
| + * MARGIN CALCULATIONS
|
| + * Margin calculations for the banner are complicated by the background Drawable's drop shadows,
|
| + * since the drop shadows are meant to be counted as being part of the margin. To deal with this,
|
| + * the margins are calculated by deducting the background Drawable's padding from the margins
|
| + * defined by the XML files.
|
| + *
|
| + * EVEN MORE LAYOUT QUIRKS
|
| + * The layout of the banner, which includes its widget sizes, may change when the screen is rotated
|
| + * to account for less screen real estate. This means that all of the View's widgets and cached
|
| + * dimensions must be rebuilt from scratch.
|
| */
|
| public class AppBannerView extends SwipableOverlayView implements View.OnClickListener {
|
| /**
|
| @@ -53,9 +73,15 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| public void onBannerClicked(AppBannerView banner);
|
| }
|
|
|
| + // Number of milliseconds to wait before showing the card highlight.
|
| + private static final long MS_HIGHLIGHT_APPEARANCE = 150;
|
| +
|
| // XML layout for the BannerView.
|
| private static final int BANNER_LAYOUT = R.layout.app_banner_view;
|
|
|
| + // Maximum distance the finger can travel before dismissing the highlight.
|
| + private static final float HIGHLIGHT_DISTANCE = 20;
|
| +
|
| // True if the layout is in left-to-right layout mode (regular mode).
|
| private final boolean mIsLayoutLTR;
|
|
|
| @@ -67,23 +93,24 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| private TextView mTitleView;
|
| private Button mButtonView;
|
| private RatingView mRatingView;
|
| - private ImageView mLogoView;
|
| + private View mLogoView;
|
| + private View mBannerHighlightView;
|
|
|
| // Information about the package.
|
| private AppData mAppData;
|
|
|
| - // Variables used during layout calculations and saved to avoid reallocations.
|
| - private final Point mSpaceMain;
|
| - private final Point mSpaceForLogo;
|
| - private final Point mSpaceForRating;
|
| - private final Point mSpaceForTitle;
|
| -
|
| // Dimension values.
|
| private int mDefinedMaxWidth;
|
| - private int mPaddingContent;
|
| - private int mMarginSide;
|
| + private int mPaddingCard;
|
| + private int mPaddingControls;
|
| + private int mMarginLeft;
|
| + private int mMarginRight;
|
| private int mMarginBottom;
|
|
|
| + // Highlight variables.
|
| + private boolean mIsBannerPressed;
|
| + private float mInitialXForHighlight;
|
| +
|
| // Initial padding values.
|
| private final Rect mBackgroundDrawablePadding;
|
|
|
| @@ -109,10 +136,6 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| public AppBannerView(Context context, AttributeSet attrs) {
|
| super(context, attrs);
|
| mIsLayoutLTR = !LocalizationUtils.isSystemLayoutDirectionRtl();
|
| - mSpaceMain = new Point();
|
| - mSpaceForLogo = new Point();
|
| - mSpaceForRating = new Point();
|
| - mSpaceForTitle = new Point();
|
|
|
| // Store the background Drawable's padding. The background used for banners is a 9-patch,
|
| // which means that it already defines padding. We need to take it into account when adding
|
| @@ -137,41 +160,38 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
|
|
| private void initializeControls() {
|
| // Cache the banner dimensions, adjusting margins for drop shadows defined in the background
|
| - // Drawable. The Drawable is defined to have the same margin on both the left and right.
|
| + // Drawable.
|
| Resources res = getResources();
|
| mDefinedMaxWidth = res.getDimensionPixelSize(R.dimen.app_banner_max_width);
|
| - mPaddingContent = res.getDimensionPixelSize(R.dimen.app_banner_padding_content);
|
| - mMarginSide = res.getDimensionPixelSize(R.dimen.app_banner_margin_sides)
|
| + mPaddingCard = res.getDimensionPixelSize(R.dimen.app_banner_padding);
|
| + mPaddingControls = res.getDimensionPixelSize(R.dimen.app_banner_padding_controls);
|
| + mMarginLeft = res.getDimensionPixelSize(R.dimen.app_banner_margin_sides)
|
| - mBackgroundDrawablePadding.left;
|
| + mMarginRight = res.getDimensionPixelSize(R.dimen.app_banner_margin_sides)
|
| + - mBackgroundDrawablePadding.right;
|
| mMarginBottom = res.getDimensionPixelSize(R.dimen.app_banner_margin_bottom)
|
| - mBackgroundDrawablePadding.bottom;
|
| if (getLayoutParams() != null) {
|
| MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
|
| - params.leftMargin = mMarginSide;
|
| - params.rightMargin = mMarginSide;
|
| + params.leftMargin = mMarginLeft;
|
| + params.rightMargin = mMarginRight;
|
| params.bottomMargin = mMarginBottom;
|
| }
|
|
|
| - // Add onto the padding defined by the Drawable's drop shadow.
|
| - int padding = res.getDimensionPixelSize(R.dimen.app_banner_padding);
|
| - int paddingStart = mBackgroundDrawablePadding.left + padding;
|
| - int paddingTop = mBackgroundDrawablePadding.top + padding;
|
| - int paddingEnd = mBackgroundDrawablePadding.right + padding;
|
| - int paddingBottom = mBackgroundDrawablePadding.bottom + padding;
|
| - ApiCompatibilityUtils.setPaddingRelative(
|
| - this, paddingStart, paddingTop, paddingEnd, paddingBottom);
|
| -
|
| // Pull out all of the controls we are expecting.
|
| mIconView = (ImageView) findViewById(R.id.app_icon);
|
| mTitleView = (TextView) findViewById(R.id.app_title);
|
| mButtonView = (Button) findViewById(R.id.app_install_button);
|
| mRatingView = (RatingView) findViewById(R.id.app_rating);
|
| - mLogoView = (ImageView) findViewById(R.id.store_logo);
|
| + mLogoView = findViewById(R.id.store_logo);
|
| + mBannerHighlightView = findViewById(R.id.banner_highlight);
|
| +
|
| assert mIconView != null;
|
| assert mTitleView != null;
|
| assert mButtonView != null;
|
| assert mLogoView != null;
|
| assert mRatingView != null;
|
| + assert mBannerHighlightView != null;
|
|
|
| // Set up the button to fire an event.
|
| mButtonView.setOnClickListener(this);
|
| @@ -199,7 +219,7 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| protected ViewGroup.MarginLayoutParams createLayoutParams() {
|
| // Define the margin around the entire banner that accounts for the drop shadow.
|
| ViewGroup.MarginLayoutParams params = super.createLayoutParams();
|
| - params.setMargins(mMarginSide, 0, mMarginSide, mMarginBottom);
|
| + params.setMargins(mMarginLeft, 0, mMarginRight, mMarginBottom);
|
| return params;
|
| }
|
|
|
| @@ -228,24 +248,25 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| void updateButtonState() {
|
| if (mButtonView == null) return;
|
|
|
| - int bgColor;
|
| + Resources res = getResources();
|
| int fgColor;
|
| String text;
|
| if (mAppData.installState() == AppData.INSTALL_STATE_INSTALLED) {
|
| - bgColor = getResources().getColor(R.color.app_banner_open_button_bg);
|
| - fgColor = getResources().getColor(R.color.app_banner_open_button_fg);
|
| - text = getResources().getString(R.string.app_banner_open);
|
| + ApiCompatibilityUtils.setBackgroundForView(mButtonView,
|
| + res.getDrawable(R.drawable.app_banner_button_open));
|
| + fgColor = res.getColor(R.color.app_banner_open_button_fg);
|
| + text = res.getString(R.string.app_banner_open);
|
| } else {
|
| - bgColor = getResources().getColor(R.color.app_banner_install_button_bg);
|
| - fgColor = getResources().getColor(R.color.app_banner_install_button_fg);
|
| + ApiCompatibilityUtils.setBackgroundForView(mButtonView,
|
| + res.getDrawable(R.drawable.app_banner_button_install));
|
| + fgColor = res.getColor(R.color.app_banner_install_button_fg);
|
| if (mAppData.installState() == AppData.INSTALL_STATE_NOT_INSTALLED) {
|
| text = mAppData.installButtonText();
|
| } else {
|
| - text = getResources().getString(R.string.app_banner_installing);
|
| + text = res.getString(R.string.app_banner_installing);
|
| }
|
| }
|
|
|
| - mButtonView.setBackgroundColor(bgColor);
|
| mButtonView.setTextColor(fgColor);
|
| mButtonView.setText(text);
|
| }
|
| @@ -260,6 +281,34 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| }
|
|
|
| /**
|
| + * Highlight the banner when the user has held it for long enough and doesn't move.
|
| + * Passes all touch events through to the parent.
|
| + */
|
| + @Override
|
| + public boolean onTouchEvent(MotionEvent event) {
|
| + int action = event.getActionMasked();
|
| + if (action == MotionEvent.ACTION_DOWN) {
|
| + mIsBannerPressed = true;
|
| + mInitialXForHighlight = event.getRawX();
|
| + getHandler().postDelayed(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + // Highlight the banner if the user is still holding onto it.
|
| + if (mIsBannerPressed) mBannerHighlightView.setVisibility(View.VISIBLE);
|
| + }
|
| + }, MS_HIGHLIGHT_APPEARANCE);
|
| + } else if (mIsBannerPressed) {
|
| + float xDifference = Math.abs(event.getRawX() - mInitialXForHighlight);
|
| + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
|
| + || (action == MotionEvent.ACTION_MOVE && xDifference > HIGHLIGHT_DISTANCE)) {
|
| + mIsBannerPressed = false;
|
| + mBannerHighlightView.setVisibility(View.INVISIBLE);
|
| + }
|
| + }
|
| +
|
| + return super.onTouchEvent(event);
|
| + }
|
| + /**
|
| * Fade the banner back into view.
|
| */
|
| @Override
|
| @@ -300,6 +349,7 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| mButtonView = null;
|
| mRatingView = null;
|
| mLogoView = null;
|
| + mBannerHighlightView = null;
|
|
|
| AppBannerView cannibalized =
|
| (AppBannerView) LayoutInflater.from(getContext()).inflate(BANNER_LAYOUT, null);
|
| @@ -313,73 +363,125 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| }
|
|
|
| /**
|
| - * Measurement for components of the banner are performed using the following procedure:
|
| + * Measures the banner and its children Views for the given space.
|
| + *
|
| + * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
| + * DPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPD
|
| + * DP...... cPD
|
| + * DP...... TITLE-------------------------cPD
|
| + * DP.ICON. ***** cPD
|
| + * DP...... LOGO BUTTONcPD
|
| + * DP...... cccccccccccccccccccccccccccccccPD
|
| + * DPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPD
|
| + * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
| *
|
| - * 00000000000000000000000000000000000000000000000000000
|
| - * 01111155555555555555555555555555555555555555555555550
|
| - * 01111155555555555555555555555555555555555555555555550
|
| - * 01111144444444444440000000000000000000000222222222220
|
| - * 01111133333333333330000000000000000000000222222222220
|
| - * 00000000000000000000000000000000000000000000000000000
|
| + * The three paddings mentioned in the class Javadoc are denoted by:
|
| + * D) Drop shadow padding.
|
| + * P) Inner card padding.
|
| + * c) Control padding.
|
| *
|
| - * 0) A maximum width is enforced on the banner, based on the smallest width of the screen,
|
| - * then padding defined by the 9-patch background Drawable is subtracted from all sides.
|
| - * 1) The icon takes up the left side of the banner.
|
| - * 2) The install button occupies the bottom-right of the banner.
|
| - * 3) The Google Play logo occupies the space to the left of the button.
|
| - * 4) The rating is assigned space above the logo and below the title.
|
| - * 5) The title is assigned whatever space is left. The maximum height of the banner is defined
|
| - * by deducting the height of either the install button or the logo + rating, (which is
|
| - * bigger). If the title cannot fit two lines comfortably, it is shrunk down to one.
|
| + * Measurement for components of the banner are performed assuming that components are laid out
|
| + * inside of the banner's background as follows:
|
| + * 1) A maximum width is enforced on the banner to keep the whole thing on screen and keep it a
|
| + * reasonable size.
|
| + * 2) The icon takes up the left side of the banner.
|
| + * 3) The install button occupies the bottom-right of the banner.
|
| + * 4) The Google Play logo occupies the space to the left of the button.
|
| + * 5) The rating is assigned space above the logo and below the title.
|
| + * 6) The title is assigned whatever space is left and sits on top of the tallest stack of
|
| + * controls.
|
| *
|
| * See {@link #android.view.View.onMeasure(int, int)} for the parameters.
|
| */
|
| @Override
|
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
| - // Enforce a maximum width on the banner.
|
| + // Enforce a maximum width on the banner, which is defined as the smallest of:
|
| + // 1) The smallest width for the device (in either landscape or portrait mode).
|
| + // 2) The defined maximum width in the dimens.xml files.
|
| + // 3) The width passed in through the MeasureSpec.
|
| Resources res = getResources();
|
| float density = res.getDisplayMetrics().density;
|
| int screenSmallestWidth = (int) (res.getConfiguration().smallestScreenWidthDp * density);
|
| int specWidth = MeasureSpec.getSize(widthMeasureSpec);
|
| - int maxWidth = Math.min(Math.min(specWidth, mDefinedMaxWidth), screenSmallestWidth);
|
| - int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
|
| -
|
| - // Track how much space is available for the banner content.
|
| - mSpaceMain.x = maxWidth - ApiCompatibilityUtils.getPaddingStart(this)
|
| - - ApiCompatibilityUtils.getPaddingEnd(this);
|
| - mSpaceMain.y = maxHeight - getPaddingTop() - getPaddingBottom();
|
| -
|
| - // Measure the icon, which hugs the banner's starting edge and defines the banner's height.
|
| - measureChildForSpace(mIconView, mSpaceMain);
|
| - mSpaceMain.x -= getChildWidthWithMargins(mIconView);
|
| - mSpaceMain.y = getChildHeightWithMargins(mIconView) + getPaddingTop() + getPaddingBottom();
|
| -
|
| - // Additional padding is defined by the mock for non-icon content on the end and bottom.
|
| - mSpaceMain.x -= mPaddingContent;
|
| - mSpaceMain.y -= mPaddingContent;
|
| -
|
| - // Measure the install button, which sits in the bottom-right corner.
|
| - measureChildForSpace(mButtonView, mSpaceMain);
|
| + int bannerWidth = Math.min(Math.min(specWidth, mDefinedMaxWidth), screenSmallestWidth);
|
| +
|
| + // Track how much space is available inside the banner's card-shaped background Drawable.
|
| + // To calculate this, we need to account for both the padding of the background (which
|
| + // is occupied by the card's drop shadows) as well as the padding defined on the inside of
|
| + // the card.
|
| + int bgPaddingWidth = mBackgroundDrawablePadding.left + mBackgroundDrawablePadding.right;
|
| + int bgPaddingHeight = mBackgroundDrawablePadding.top + mBackgroundDrawablePadding.bottom;
|
| + final int maxControlWidth = bannerWidth - bgPaddingWidth - (mPaddingCard * 2);
|
| +
|
| + // Control height is constrained to provide a reasonable aspect ratio.
|
| + // In practice, the only controls which can cause an issue are the title and the install
|
| + // button, since they have strings that can change size according to user preference. The
|
| + // other controls are all defined to be a certain height.
|
| + int specHeight = MeasureSpec.getSize(heightMeasureSpec);
|
| + int reasonableHeight = maxControlWidth / 4;
|
| + int paddingHeight = bgPaddingHeight + (mPaddingCard * 2);
|
| + final int maxControlHeight = Math.min(specHeight, reasonableHeight) - paddingHeight;
|
| + final int maxStackedControlHeight = maxControlWidth / 3;
|
| +
|
| + // Determine how big each component wants to be. The icon is measured separately because
|
| + // it is not stacked with the other controls.
|
| + measureChildForSpace(mIconView, maxControlWidth, maxControlHeight);
|
| + for (int i = 0; i < getChildCount(); i++) {
|
| + if (getChildAt(i) != mIconView) {
|
| + measureChildForSpace(getChildAt(i), maxControlWidth, maxStackedControlHeight);
|
| + }
|
| + }
|
|
|
| - // Measure the logo, which sits in the bottom-left corner next to the icon.
|
| - mSpaceForLogo.x = mSpaceMain.x - getChildWidthWithMargins(mButtonView);
|
| - mSpaceForLogo.y = mSpaceMain.y;
|
| - measureChildForSpace(mLogoView, mSpaceForLogo);
|
| + // Determine how tall the banner needs to be to fit everything by calculating the combined
|
| + // height of the stacked controls. There are three competing stacks to measure:
|
| + // 1) The icon.
|
| + // 2) The app title + control padding + star rating + store logo.
|
| + // 3) The app title + control padding + install button.
|
| + // The control padding is extra padding that applies only to the non-icon widgets.
|
| + int iconStackHeight = getHeightWithMargins(mIconView);
|
| + int logoStackHeight = getHeightWithMargins(mTitleView) + mPaddingControls
|
| + + getHeightWithMargins(mRatingView) + getHeightWithMargins(mLogoView);
|
| + int buttonStackHeight = getHeightWithMargins(mTitleView) + mPaddingControls
|
| + + getHeightWithMargins(mButtonView);
|
| + int biggestStackHeight =
|
| + Math.max(iconStackHeight, Math.max(logoStackHeight, buttonStackHeight));
|
| +
|
| + // The icon hugs the banner's starting edge, from the top of the banner to the bottom.
|
| + final int iconSize = biggestStackHeight;
|
| + measureChildForSpaceExactly(mIconView, iconSize, iconSize);
|
| +
|
| + // The rest of the content is laid out to the right of the icon.
|
| + // Additional padding is defined for non-icon content on the end and bottom.
|
| + final int contentWidth =
|
| + maxControlWidth - getWidthWithMargins(mIconView) - mPaddingControls;
|
| + final int contentHeight = biggestStackHeight - mPaddingControls;
|
| + measureChildForSpace(mButtonView, contentWidth, contentHeight);
|
| + measureChildForSpace(mLogoView, contentWidth, contentHeight);
|
|
|
| // Measure the star rating, which sits below the title and above the logo.
|
| - mSpaceForRating.x = mSpaceForLogo.x;
|
| - mSpaceForRating.y = mSpaceForLogo.y - getChildHeightWithMargins(mLogoView);
|
| - measureChildForSpace(mRatingView, mSpaceForRating);
|
| -
|
| - // The app title spans the top of the banner.
|
| - mSpaceForTitle.x = mSpaceMain.x;
|
| - mSpaceForTitle.y = mSpaceMain.y - getChildHeightWithMargins(mLogoView)
|
| - - getChildHeightWithMargins(mRatingView);
|
| - measureChildForSpace(mTitleView, mSpaceForTitle);
|
| -
|
| - // Set the measured dimensions for the banner.
|
| - int measuredHeight = mIconView.getMeasuredHeight() + getPaddingTop() + getPaddingBottom();
|
| - setMeasuredDimension(maxWidth, measuredHeight);
|
| + final int ratingWidth = contentWidth;
|
| + final int ratingHeight = contentHeight - getHeightWithMargins(mLogoView);
|
| + measureChildForSpace(mRatingView, ratingWidth, ratingHeight);
|
| +
|
| + // The app title spans the top of the banner and sits on top of the other controls.
|
| + int biggerStack = Math.max(getHeightWithMargins(mButtonView),
|
| + getHeightWithMargins(mLogoView) + getHeightWithMargins(mRatingView));
|
| + final int titleWidth = contentWidth;
|
| + final int titleHeight = contentHeight - biggerStack;
|
| + measureChildForSpace(mTitleView, titleWidth, titleHeight);
|
| +
|
| + // Set the measured dimensions for the banner. The banner's height is defined by the
|
| + // tallest stack of components, the padding of the banner's card background, and the extra
|
| + // padding around the banner's components.
|
| + int bannerPadding = mBackgroundDrawablePadding.top + mBackgroundDrawablePadding.bottom
|
| + + (mPaddingCard * 2);
|
| + int bannerHeight = biggestStackHeight + bannerPadding;
|
| + setMeasuredDimension(bannerWidth, bannerHeight);
|
| +
|
| + // Make the banner highlight view be the exact same size as the banner's card background.
|
| + final int cardWidth = bannerWidth - bgPaddingWidth;
|
| + final int cardHeight = bannerHeight - bgPaddingHeight;
|
| + measureChildForSpaceExactly(mBannerHighlightView, cardWidth, cardHeight);
|
| }
|
|
|
| /**
|
| @@ -389,21 +491,29 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| @Override
|
| protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
| super.onLayout(changed, l, t, r, b);
|
| + int top = mBackgroundDrawablePadding.top;
|
| + int bottom = getMeasuredHeight() - mBackgroundDrawablePadding.bottom;
|
| + int start = mBackgroundDrawablePadding.left;
|
| + int end = getMeasuredWidth() - mBackgroundDrawablePadding.right;
|
| +
|
| + // The highlight overlay covers the entire banner (minus drop shadow padding).
|
| + mBannerHighlightView.layout(start, top, end, bottom);
|
|
|
| - int top = getPaddingTop();
|
| - int bottom = getMeasuredHeight() - getPaddingBottom();
|
| - int start = ApiCompatibilityUtils.getPaddingStart(this);
|
| - int end = getMeasuredWidth() - ApiCompatibilityUtils.getPaddingEnd(this);
|
| + // Apply the padding for the rest of the widgets.
|
| + top += mPaddingCard;
|
| + bottom -= mPaddingCard;
|
| + start += mPaddingCard;
|
| + end -= mPaddingCard;
|
|
|
| // Lay out the icon.
|
| int iconWidth = mIconView.getMeasuredWidth();
|
| int iconLeft = mIsLayoutLTR ? start : (getMeasuredWidth() - start - iconWidth);
|
| mIconView.layout(iconLeft, top, iconLeft + iconWidth, top + mIconView.getMeasuredHeight());
|
| - start += getChildWidthWithMargins(mIconView);
|
| + start += getWidthWithMargins(mIconView);
|
|
|
| - // Factor in the additional padding.
|
| - end -= mPaddingContent;
|
| - bottom -= mPaddingContent;
|
| + // Factor in the additional padding, which is only tacked onto the end and bottom.
|
| + end -= mPaddingControls;
|
| + bottom -= mPaddingControls;
|
|
|
| // Lay out the app title text.
|
| int titleWidth = mTitleView.getMeasuredWidth();
|
| @@ -440,37 +550,74 @@ public class AppBannerView extends SwipableOverlayView implements View.OnClickLi
|
| }
|
|
|
| /**
|
| + * Measures a child for the given space, accounting for defined heights and margins.
|
| + * @param child View to measure.
|
| + * @param availableWidth Available width for the view.
|
| + * @param availableHeight Available height for the view.
|
| + */
|
| + private void measureChildForSpace(View child, int availableWidth, int availableHeight) {
|
| + // Handle margins.
|
| + availableWidth -= getMarginWidth(child);
|
| + availableHeight -= getMarginHeight(child);
|
| +
|
| + // Account for any layout-defined dimensions for the view.
|
| + int childWidth = child.getLayoutParams().width;
|
| + int childHeight = child.getLayoutParams().height;
|
| + if (childWidth >= 0) availableWidth = Math.min(availableWidth, childWidth);
|
| + if (childHeight >= 0) availableHeight = Math.min(availableHeight, childHeight);
|
| +
|
| + int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
|
| + int heightSpec = MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.AT_MOST);
|
| + child.measure(widthSpec, heightSpec);
|
| + }
|
| +
|
| + /**
|
| + * Forces a child to exactly occupy the given space.
|
| + * @param child View to measure.
|
| + * @param availableWidth Available width for the view.
|
| + * @param availableHeight Available height for the view.
|
| + */
|
| + private void measureChildForSpaceExactly(View child, int availableWidth, int availableHeight) {
|
| + int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY);
|
| + int heightSpec = MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY);
|
| + child.measure(widthSpec, heightSpec);
|
| + }
|
| +
|
| + /**
|
| + * Calculates how wide the margins are for the given View.
|
| + * @param view View to measure.
|
| + * @return Measured width of the margins.
|
| + */
|
| + private static int getMarginWidth(View view) {
|
| + MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
|
| + return params.leftMargin + params.rightMargin;
|
| + }
|
| +
|
| + /**
|
| * Calculates how wide the given View has been measured to be, including its margins.
|
| - * @param child Child to measure.
|
| - * @return Measured width of the child plus its margins.
|
| + * @param view View to measure.
|
| + * @return Measured width of the view plus its margins.
|
| */
|
| - private int getChildWidthWithMargins(View child) {
|
| - MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
|
| - return child.getMeasuredWidth() + ApiCompatibilityUtils.getMarginStart(params)
|
| - + ApiCompatibilityUtils.getMarginEnd(params);
|
| + private static int getWidthWithMargins(View view) {
|
| + return view.getMeasuredWidth() + getMarginWidth(view);
|
| }
|
|
|
| /**
|
| - * Calculates how tall the given View has been measured to be, including its margins.
|
| - * @param child Child to measure.
|
| - * @return Measured height of the child plus its margins.
|
| + * Calculates how tall the margins are for the given View.
|
| + * @param view View to measure.
|
| + * @return Measured height of the margins.
|
| */
|
| - private static int getChildHeightWithMargins(View child) {
|
| - MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
|
| - return child.getMeasuredHeight() + params.topMargin + params.bottomMargin;
|
| + private static int getMarginHeight(View view) {
|
| + MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
|
| + return params.topMargin + params.bottomMargin;
|
| }
|
|
|
| /**
|
| - * Measures a child so that it fits within the given space, taking into account heights defined
|
| - * in the layout.
|
| - * @param child View to measure.
|
| - * @param available Available space, with width stored in the x coordinate and height in the y.
|
| + * Calculates how tall the given View has been measured to be, including its margins.
|
| + * @param view View to measure.
|
| + * @return Measured height of the view plus its margins.
|
| */
|
| - private void measureChildForSpace(View child, Point available) {
|
| - int childHeight = child.getLayoutParams().height;
|
| - int maxHeight = childHeight > 0 ? Math.min(available.y, childHeight) : available.y;
|
| - int widthSpec = MeasureSpec.makeMeasureSpec(available.x, MeasureSpec.AT_MOST);
|
| - int heightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
|
| - measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
|
| + private static int getHeightWithMargins(View view) {
|
| + return view.getMeasuredHeight() + getMarginHeight(view);
|
| }
|
| }
|
|
|