| 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
|
| deleted file mode 100644
|
| index 93e77435e2d12f68b0864a03b8d39c956be2694e..0000000000000000000000000000000000000000
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerView.java
|
| +++ /dev/null
|
| @@ -1,855 +0,0 @@
|
| -// Copyright 2014 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.banners;
|
| -
|
| -import android.animation.ObjectAnimator;
|
| -import android.app.Activity;
|
| -import android.app.PendingIntent;
|
| -import android.content.ActivityNotFoundException;
|
| -import android.content.ContentResolver;
|
| -import android.content.Context;
|
| -import android.content.Intent;
|
| -import android.content.IntentSender;
|
| -import android.content.pm.PackageManager;
|
| -import android.content.res.Configuration;
|
| -import android.content.res.Resources;
|
| -import android.graphics.Rect;
|
| -import android.os.Looper;
|
| -import android.util.AttributeSet;
|
| -import android.util.Log;
|
| -import android.view.LayoutInflater;
|
| -import android.view.MotionEvent;
|
| -import android.view.View;
|
| -import android.view.ViewConfiguration;
|
| -import android.view.ViewGroup;
|
| -import android.widget.Button;
|
| -import android.widget.ImageButton;
|
| -import android.widget.ImageView;
|
| -import android.widget.TextView;
|
| -
|
| -import org.chromium.base.ApiCompatibilityUtils;
|
| -import org.chromium.chrome.R;
|
| -import org.chromium.content.browser.ContentViewCore;
|
| -import org.chromium.ui.base.LocalizationUtils;
|
| -import org.chromium.ui.base.WindowAndroid;
|
| -import org.chromium.ui.base.WindowAndroid.IntentCallback;
|
| -
|
| -/**
|
| - * Lays out a banner for showing info about an app on the Play Store.
|
| - * 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.
|
| - *
|
| - * TODO(dfalcantara): Nuke this file.
|
| - */
|
| -public class AppBannerView extends SwipableOverlayView
|
| - implements View.OnClickListener, InstallerDelegate.Observer, IntentCallback {
|
| - private static final String TAG = "AppBannerView";
|
| -
|
| - /**
|
| - * Class that is alerted about things happening to the BannerView.
|
| - */
|
| - public static interface Observer {
|
| - /**
|
| - * Called when the banner is removed from the hierarchy.
|
| - * @param banner Banner being dismissed.
|
| - */
|
| - public void onBannerRemoved(AppBannerView banner);
|
| -
|
| - /**
|
| - * Called when the user manually closes a banner.
|
| - * @param banner Banner being blocked.
|
| - * @param url URL of the page that requested the banner.
|
| - * @param packageName Name of the app's package.
|
| - */
|
| - public void onBannerBlocked(AppBannerView banner, String url, String packageName);
|
| -
|
| - /**
|
| - * Called when the banner begins to be dismissed.
|
| - * @param banner Banner being closed.
|
| - * @param dismissType Type of dismissal performed.
|
| - */
|
| - public void onBannerDismissEvent(AppBannerView banner, int dismissType);
|
| -
|
| - /**
|
| - * Called when an install event has occurred.
|
| - */
|
| - public void onBannerInstallEvent(AppBannerView banner, int eventType);
|
| -
|
| - /**
|
| - * Called when the banner needs to have an Activity started for a result.
|
| - * @param banner Banner firing the event.
|
| - * @param intent Intent to fire.
|
| - */
|
| - public boolean onFireIntent(AppBannerView banner, PendingIntent intent);
|
| - }
|
| -
|
| - // Installation states.
|
| - private static final int INSTALL_STATE_NOT_INSTALLED = 0;
|
| - private static final int INSTALL_STATE_INSTALLING = 1;
|
| - private static final int INSTALL_STATE_INSTALLED = 2;
|
| -
|
| - // XML layout for the BannerView.
|
| - private static final int BANNER_LAYOUT = R.layout.app_banner_view;
|
| -
|
| - // True if the layout is in left-to-right layout mode (regular mode).
|
| - private final boolean mIsLayoutLTR;
|
| -
|
| - // Class to alert about BannerView events.
|
| - private AppBannerView.Observer mObserver;
|
| -
|
| - // Information about the package. Shouldn't ever be null after calling {@link #initialize()}.
|
| - private AppData mAppData;
|
| -
|
| - // Views comprising the app banner.
|
| - private ViewGroup mContainerView;
|
| - private ImageView mIconView;
|
| - private TextView mTitleView;
|
| - private Button mInstallButtonView;
|
| - private RatingView mRatingView;
|
| - private View mLogoView;
|
| - private View mBannerHighlightView;
|
| - private ImageButton mCloseButtonView;
|
| -
|
| - // Dimension values.
|
| - private int mDefinedMaxWidth;
|
| - private int mPaddingCard;
|
| - private int mPaddingControls;
|
| - private int mMarginLeft;
|
| - private int mMarginRight;
|
| - private int mMarginBottom;
|
| - private int mTouchSlop;
|
| -
|
| - // Highlight variables.
|
| - private boolean mIsBannerPressed;
|
| - private float mInitialXForHighlight;
|
| -
|
| - // Initial padding values.
|
| - private final Rect mBackgroundDrawablePadding;
|
| -
|
| - // Install tracking.
|
| - private boolean mWasInstallDialogShown;
|
| - private InstallerDelegate mInstallTask;
|
| - private int mInstallState;
|
| -
|
| - /**
|
| - * Creates a BannerView and adds it to the given ContentViewCore.
|
| - * @param contentViewCore ContentViewCore to display the AppBannerView for.
|
| - * @param observer Class that is alerted for AppBannerView events.
|
| - * @param data Data about the app.
|
| - * @return The created banner.
|
| - */
|
| - public static AppBannerView create(
|
| - ContentViewCore contentViewCore, Observer observer, AppData data) {
|
| - Context context = contentViewCore.getContext().getApplicationContext();
|
| - AppBannerView banner =
|
| - (AppBannerView) LayoutInflater.from(context).inflate(BANNER_LAYOUT, null);
|
| - banner.initialize(observer, data);
|
| - banner.setContentViewCore(contentViewCore);
|
| - banner.addToParentView(contentViewCore.getContainerView());
|
| - return banner;
|
| - }
|
| -
|
| - /**
|
| - * Creates a BannerView from an XML layout.
|
| - */
|
| - public AppBannerView(Context context, AttributeSet attrs) {
|
| - super(context, attrs);
|
| - mIsLayoutLTR = !LocalizationUtils.isLayoutRtl();
|
| -
|
| - // 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
|
| - // even more padding to the inside of it.
|
| - mBackgroundDrawablePadding = new Rect();
|
| - mBackgroundDrawablePadding.left = ApiCompatibilityUtils.getPaddingStart(this);
|
| - mBackgroundDrawablePadding.right = ApiCompatibilityUtils.getPaddingEnd(this);
|
| - mBackgroundDrawablePadding.top = getPaddingTop();
|
| - mBackgroundDrawablePadding.bottom = getPaddingBottom();
|
| -
|
| - mInstallState = INSTALL_STATE_NOT_INSTALLED;
|
| - }
|
| -
|
| - /**
|
| - * Initialize the banner with information about the package.
|
| - * @param observer Class to alert about changes to the banner.
|
| - * @param data Information about the app being advertised.
|
| - */
|
| - private void initialize(Observer observer, AppData data) {
|
| - mObserver = observer;
|
| - mAppData = data;
|
| - initializeControls();
|
| - }
|
| -
|
| - private void initializeControls() {
|
| - // Cache the banner dimensions, adjusting margins for drop shadows defined in the background
|
| - // Drawable.
|
| - Resources res = getResources();
|
| - mDefinedMaxWidth = res.getDimensionPixelSize(R.dimen.app_banner_max_width);
|
| - 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 = mMarginLeft;
|
| - params.rightMargin = mMarginRight;
|
| - params.bottomMargin = mMarginBottom;
|
| - }
|
| -
|
| - // Pull out all of the controls we are expecting.
|
| - mContainerView = (ViewGroup) findViewById(R.id.banner_container);
|
| - mIconView = (ImageView) findViewById(R.id.app_icon);
|
| - mTitleView = (TextView) findViewById(R.id.app_title);
|
| - mInstallButtonView = (Button) findViewById(R.id.app_install_button);
|
| - mRatingView = (RatingView) findViewById(R.id.app_rating);
|
| - mLogoView = findViewById(R.id.store_logo);
|
| - mBannerHighlightView = findViewById(R.id.banner_highlight);
|
| - mCloseButtonView = (ImageButton) findViewById(R.id.close_button);
|
| -
|
| - assert mIconView != null;
|
| - assert mTitleView != null;
|
| - assert mInstallButtonView != null;
|
| - assert mLogoView != null;
|
| - assert mRatingView != null;
|
| - assert mBannerHighlightView != null;
|
| - assert mCloseButtonView != null;
|
| -
|
| - // Set up the buttons to fire an event.
|
| - mInstallButtonView.setOnClickListener(this);
|
| - mCloseButtonView.setOnClickListener(this);
|
| -
|
| - // Configure the controls with the package information.
|
| - mTitleView.setText(mAppData.title());
|
| - mRatingView.initialize(mAppData.rating());
|
| -
|
| - // Determine how much the user can drag sideways before their touch is considered a scroll.
|
| - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
| -
|
| - // Set up the install button.
|
| - updateButtonStatus();
|
| - }
|
| -
|
| - @Override
|
| - public void onClick(View view) {
|
| - if (mObserver == null) return;
|
| -
|
| - // Only allow the button to be clicked when the banner's in a neutral position.
|
| - if (Math.abs(getTranslationX()) > ZERO_THRESHOLD
|
| - || Math.abs(getTranslationY()) > ZERO_THRESHOLD) {
|
| - return;
|
| - }
|
| -
|
| - if (view == mInstallButtonView) {
|
| - // Check that nothing happened in the background to change the install state of the app.
|
| - int previousState = mInstallState;
|
| - updateButtonStatus();
|
| - if (mInstallState != previousState) return;
|
| -
|
| - // Ignore button clicks when the app is installing.
|
| - if (mInstallState == INSTALL_STATE_INSTALLING) return;
|
| -
|
| - mInstallButtonView.setEnabled(false);
|
| -
|
| - if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
|
| - // The user initiated an install. Track it happening only once.
|
| - if (!mWasInstallDialogShown) {
|
| - mObserver.onBannerInstallEvent(this, AppBannerMetricsIds.INSTALL_TRIGGERED);
|
| - mWasInstallDialogShown = true;
|
| - }
|
| -
|
| - if (mObserver.onFireIntent(this, mAppData.installIntent())) {
|
| - // Temporarily hide the banner.
|
| - createVerticalSnapAnimation(false);
|
| - } else {
|
| - Log.e(TAG, "Failed to fire install intent.");
|
| - dismiss(AppBannerMetricsIds.DISMISS_ERROR);
|
| - }
|
| - } else if (mInstallState == INSTALL_STATE_INSTALLED) {
|
| - // The app is installed. Open it.
|
| - try {
|
| - Intent appIntent = getAppLaunchIntent();
|
| - if (appIntent != null) getContext().startActivity(appIntent);
|
| - } catch (ActivityNotFoundException e) {
|
| - Log.e(TAG, "Failed to find app package: " + mAppData.packageName());
|
| - }
|
| -
|
| - dismiss(AppBannerMetricsIds.DISMISS_APP_OPEN);
|
| - }
|
| - } else if (view == mCloseButtonView) {
|
| - if (mObserver != null) {
|
| - mObserver.onBannerBlocked(this, mAppData.siteUrl(), mAppData.packageName());
|
| - }
|
| -
|
| - dismiss(AppBannerMetricsIds.DISMISS_CLOSE_BUTTON);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void onViewSwipedAway() {
|
| - if (mObserver == null) return;
|
| - mObserver.onBannerDismissEvent(this, AppBannerMetricsIds.DISMISS_BANNER_SWIPE);
|
| - mObserver.onBannerBlocked(this, mAppData.siteUrl(), mAppData.packageName());
|
| - }
|
| -
|
| - @Override
|
| - protected void onViewClicked() {
|
| - // Send the user to the app's Play store page.
|
| - try {
|
| - IntentSender sender = mAppData.detailsIntent().getIntentSender();
|
| - getContext().startIntentSender(sender, new Intent(), 0, 0, 0);
|
| - } catch (IntentSender.SendIntentException e) {
|
| - Log.e(TAG, "Failed to launch details intent.");
|
| - }
|
| -
|
| - dismiss(AppBannerMetricsIds.DISMISS_BANNER_CLICK);
|
| - }
|
| -
|
| - @Override
|
| - protected void onViewPressed(MotionEvent event) {
|
| - // Highlight the banner when the user has held it for long enough and doesn't move.
|
| - mInitialXForHighlight = event.getRawX();
|
| - mIsBannerPressed = true;
|
| - mBannerHighlightView.setVisibility(View.VISIBLE);
|
| - }
|
| -
|
| - @Override
|
| - public void onIntentCompleted(WindowAndroid window, int resultCode,
|
| - ContentResolver contentResolver, Intent data) {
|
| - if (isDismissed()) return;
|
| -
|
| - createVerticalSnapAnimation(true);
|
| - if (resultCode == Activity.RESULT_OK) {
|
| - // The user chose to install the app. Watch the PackageManager to see when it finishes
|
| - // installing it.
|
| - mObserver.onBannerInstallEvent(this, AppBannerMetricsIds.INSTALL_STARTED);
|
| -
|
| - PackageManager pm = getContext().getPackageManager();
|
| - mInstallTask =
|
| - new InstallerDelegate(Looper.getMainLooper(), pm, this, mAppData.packageName());
|
| - mInstallTask.start();
|
| - mInstallState = INSTALL_STATE_INSTALLING;
|
| - }
|
| - updateButtonStatus();
|
| - }
|
| -
|
| -
|
| - @Override
|
| - public void onInstallFinished(InstallerDelegate monitor, boolean success) {
|
| - if (isDismissed() || mInstallTask != monitor) return;
|
| -
|
| - if (success) {
|
| - // Let the user open the app from here.
|
| - mObserver.onBannerInstallEvent(this, AppBannerMetricsIds.INSTALL_COMPLETED);
|
| - mInstallState = INSTALL_STATE_INSTALLED;
|
| - updateButtonStatus();
|
| - } else {
|
| - dismiss(AppBannerMetricsIds.DISMISS_INSTALL_TIMEOUT);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public ViewGroup.MarginLayoutParams createLayoutParams() {
|
| - // Define the margin around the entire banner that accounts for the drop shadow.
|
| - ViewGroup.MarginLayoutParams params = super.createLayoutParams();
|
| - params.setMargins(mMarginLeft, 0, mMarginRight, mMarginBottom);
|
| - return params;
|
| - }
|
| -
|
| - /**
|
| - * Removes this View from its parent and alerts any observers of the dismissal.
|
| - * @return Whether or not the View was successfully dismissed.
|
| - */
|
| - @Override
|
| - public boolean removeFromParentView() {
|
| - if (super.removeFromParentView()) {
|
| - mObserver.onBannerRemoved(this);
|
| - destroy();
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Dismisses the banner.
|
| - * @param eventType Event that triggered the dismissal. See {@link AppBannerMetricsIds}.
|
| - */
|
| - public void dismiss(int eventType) {
|
| - if (isDismissed() || mObserver == null) return;
|
| -
|
| - dismiss(eventType == AppBannerMetricsIds.DISMISS_CLOSE_BUTTON);
|
| - mObserver.onBannerDismissEvent(this, eventType);
|
| - }
|
| -
|
| - /**
|
| - * Destroys the Banner.
|
| - */
|
| - public void destroy() {
|
| - if (!isDismissed()) dismiss(AppBannerMetricsIds.DISMISS_ERROR);
|
| -
|
| - if (mInstallTask != null) {
|
| - mInstallTask.cancel();
|
| - mInstallTask = null;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Updates the install button (install state, text, color, etc.).
|
| - */
|
| - void updateButtonStatus() {
|
| - if (mInstallButtonView == null) return;
|
| -
|
| - // Determine if the saved install status of the app is out of date.
|
| - // It is not easily possible to detect if an app is in the process of being installed, so we
|
| - // can't properly transition to that state from here.
|
| - if (getAppLaunchIntent() == null) {
|
| - if (mInstallState == INSTALL_STATE_INSTALLED) {
|
| - mInstallState = INSTALL_STATE_NOT_INSTALLED;
|
| - }
|
| - } else {
|
| - mInstallState = INSTALL_STATE_INSTALLED;
|
| - }
|
| -
|
| - // Update what the button looks like.
|
| - Resources res = getResources();
|
| - int fgColor;
|
| - String text;
|
| - if (mInstallState == INSTALL_STATE_INSTALLED) {
|
| - ApiCompatibilityUtils.setBackgroundForView(mInstallButtonView,
|
| - 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 {
|
| - ApiCompatibilityUtils.setBackgroundForView(mInstallButtonView,
|
| - res.getDrawable(R.drawable.app_banner_button_install));
|
| - fgColor = res.getColor(R.color.app_banner_install_button_fg);
|
| - if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
|
| - text = mAppData.installButtonText();
|
| - mInstallButtonView.setContentDescription(
|
| - getContext().getString(R.string.app_banner_install_accessibility, text));
|
| - } else {
|
| - text = res.getString(R.string.app_banner_installing);
|
| - }
|
| - }
|
| -
|
| - mInstallButtonView.setTextColor(fgColor);
|
| - mInstallButtonView.setText(text);
|
| - mInstallButtonView.setEnabled(mInstallState != INSTALL_STATE_INSTALLING);
|
| - }
|
| -
|
| - /**
|
| - * Determine how big an icon needs to be for the Layout.
|
| - * @param context Context to grab resources from.
|
| - * @return How big the icon is expected to be, in pixels.
|
| - */
|
| - static int getIconSize(Context context) {
|
| - return context.getResources().getDimensionPixelSize(R.dimen.app_banner_icon_size);
|
| - }
|
| -
|
| - /**
|
| - * Passes all touch events through to the parent.
|
| - */
|
| - @Override
|
| - public boolean onTouchEvent(MotionEvent event) {
|
| - int action = event.getActionMasked();
|
| - if (mIsBannerPressed) {
|
| - // Mimic Google Now card behavior, where the card stops being highlighted if the user
|
| - // scrolls a bit to the side.
|
| - float xDifference = Math.abs(event.getRawX() - mInitialXForHighlight);
|
| - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
|
| - || (action == MotionEvent.ACTION_MOVE && xDifference > mTouchSlop)) {
|
| - mIsBannerPressed = false;
|
| - mBannerHighlightView.setVisibility(View.INVISIBLE);
|
| - }
|
| - }
|
| -
|
| - return super.onTouchEvent(event);
|
| - }
|
| -
|
| - /**
|
| - * Fade the banner back into view.
|
| - */
|
| - @Override
|
| - protected void onAttachedToWindow() {
|
| - super.onAttachedToWindow();
|
| - ObjectAnimator.ofFloat(this, "alpha", getAlpha(), 1.f).setDuration(
|
| - MS_ANIMATION_DURATION).start();
|
| - setVisibility(VISIBLE);
|
| - }
|
| -
|
| - /**
|
| - * Immediately hide the banner to avoid having them show up in snapshots.
|
| - */
|
| - @Override
|
| - protected void onDetachedFromWindow() {
|
| - super.onDetachedFromWindow();
|
| - setAlpha(0.0f);
|
| - setVisibility(INVISIBLE);
|
| - }
|
| -
|
| - /**
|
| - * Watch for changes in the available screen height, which triggers a complete recreation of the
|
| - * banner widgets. This is mainly due to the fact that the Nexus 7 has a smaller banner defined
|
| - * for its landscape versus its portrait layouts.
|
| - */
|
| - @Override
|
| - protected void onConfigurationChanged(Configuration config) {
|
| - super.onConfigurationChanged(config);
|
| -
|
| - if (isDismissed()) return;
|
| -
|
| - // If the card's maximum width hasn't changed, the individual views can't have, either.
|
| - int newDefinedWidth = getResources().getDimensionPixelSize(R.dimen.app_banner_max_width);
|
| - if (mDefinedMaxWidth == newDefinedWidth) return;
|
| -
|
| - // Cannibalize another version of this layout to get Views using the new resources and
|
| - // sizes.
|
| - while (getChildCount() > 0) removeViewAt(0);
|
| - mIconView = null;
|
| - mTitleView = null;
|
| - mInstallButtonView = null;
|
| - mRatingView = null;
|
| - mLogoView = null;
|
| - mBannerHighlightView = null;
|
| -
|
| - AppBannerView cannibalized =
|
| - (AppBannerView) LayoutInflater.from(getContext()).inflate(BANNER_LAYOUT, null);
|
| - while (cannibalized.getChildCount() > 0) {
|
| - View child = cannibalized.getChildAt(0);
|
| - cannibalized.removeViewAt(0);
|
| - addView(child);
|
| - }
|
| - initializeControls();
|
| - requestLayout();
|
| - }
|
| -
|
| - @Override
|
| - public void onWindowFocusChanged(boolean hasWindowFocus) {
|
| - if (hasWindowFocus) updateButtonStatus();
|
| - }
|
| -
|
| - /**
|
| - * @return Intent to launch the app that is being promoted.
|
| - */
|
| - private Intent getAppLaunchIntent() {
|
| - String packageName = mAppData.packageName();
|
| - PackageManager packageManager = getContext().getPackageManager();
|
| - return packageManager.getLaunchIntentForPackage(packageName);
|
| - }
|
| -
|
| - /**
|
| - * Measures the banner and its children Views for the given space.
|
| - *
|
| - * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
| - * DPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPD
|
| - * DP...... cPD
|
| - * DP...... TITLE----------------------- XcPD
|
| - * DP.ICON. ***** cPD
|
| - * DP...... LOGO BUTTONcPD
|
| - * DP...... cccccccccccccccccccccccccccccccPD
|
| - * DPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPD
|
| - * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
| - *
|
| - * The three paddings mentioned in the class Javadoc are denoted by:
|
| - * D) Drop shadow padding.
|
| - * P) Inner card padding.
|
| - * c) Control padding.
|
| - *
|
| - * 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 close button (if visible) sits in the top right of the banner.
|
| - * 7) 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, 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 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 < mContainerView.getChildCount(); i++) {
|
| - View child = mContainerView.getChildAt(i);
|
| - if (child != mIconView) {
|
| - measureChildForSpace(child, maxControlWidth, maxStackedControlHeight);
|
| - }
|
| - }
|
| -
|
| - // 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.
|
| - // The close button does not get counted as part of a stack.
|
| - int iconStackHeight = getHeightWithMargins(mIconView);
|
| - int logoStackHeight = getHeightWithMargins(mTitleView) + mPaddingControls
|
| - + getHeightWithMargins(mRatingView) + getHeightWithMargins(mLogoView);
|
| - int buttonStackHeight = getHeightWithMargins(mTitleView) + mPaddingControls
|
| - + getHeightWithMargins(mInstallButtonView);
|
| - 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(mLogoView, contentWidth, contentHeight);
|
| -
|
| - // Restrict the button size to prevent overrunning the Google Play logo.
|
| - int remainingButtonWidth =
|
| - maxControlWidth - getWidthWithMargins(mLogoView) - getWidthWithMargins(mIconView);
|
| - mInstallButtonView.setMaxWidth(remainingButtonWidth);
|
| - measureChildForSpace(mInstallButtonView, contentWidth, contentHeight);
|
| -
|
| - // Measure the star rating, which sits below the title and above the logo.
|
| - final int ratingWidth = contentWidth;
|
| - final int ratingHeight = contentHeight - getHeightWithMargins(mLogoView);
|
| - measureChildForSpace(mRatingView, ratingWidth, ratingHeight);
|
| -
|
| - // The close button sits to the right of the title and above the install button.
|
| - final int closeWidth = contentWidth;
|
| - final int closeHeight = contentHeight - getHeightWithMargins(mInstallButtonView);
|
| - measureChildForSpace(mCloseButtonView, closeWidth, closeHeight);
|
| -
|
| - // The app title spans the top of the banner and sits on top of the other controls, and to
|
| - // the left of the close button. The computation for the width available to the title is
|
| - // complicated by how the button sits in the corner and absorbs the padding that would
|
| - // normally be there.
|
| - int biggerStack = Math.max(getHeightWithMargins(mInstallButtonView),
|
| - getHeightWithMargins(mLogoView) + getHeightWithMargins(mRatingView));
|
| - final int titleWidth = contentWidth - getWidthWithMargins(mCloseButtonView) + mPaddingCard;
|
| - 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);
|
| - measureChildForSpaceExactly(mContainerView, 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);
|
| - }
|
| -
|
| - /**
|
| - * Lays out the controls according to the algorithm in {@link #onMeasure}.
|
| - * See {@link #android.view.View.onLayout(boolean, int, int, int, int)} for the parameters.
|
| - */
|
| - @Override
|
| - protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
| - super.onLayout(changed, l, t, r, b);
|
| - mContainerView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
| -
|
| - 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);
|
| -
|
| - // Lay out the close button in the top-right corner. Padding that would normally go to the
|
| - // card is applied to the close button so that it has a bigger touch target.
|
| - if (mCloseButtonView.getVisibility() == VISIBLE) {
|
| - int closeWidth = mCloseButtonView.getMeasuredWidth();
|
| - int closeTop =
|
| - top + ((MarginLayoutParams) mCloseButtonView.getLayoutParams()).topMargin;
|
| - int closeBottom = closeTop + mCloseButtonView.getMeasuredHeight();
|
| - int closeRight = mIsLayoutLTR ? end : (getMeasuredWidth() - end + closeWidth);
|
| - int closeLeft = closeRight - closeWidth;
|
| - mCloseButtonView.layout(closeLeft, closeTop, closeRight, closeBottom);
|
| - }
|
| -
|
| - // 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 += getWidthWithMargins(mIconView);
|
| -
|
| - // 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();
|
| - int titleTop = top + ((MarginLayoutParams) mTitleView.getLayoutParams()).topMargin;
|
| - int titleBottom = titleTop + mTitleView.getMeasuredHeight();
|
| - int titleLeft = mIsLayoutLTR ? start : (getMeasuredWidth() - start - titleWidth);
|
| - mTitleView.layout(titleLeft, titleTop, titleLeft + titleWidth, titleBottom);
|
| -
|
| - // The mock shows the margin eating into the descender area of the TextView.
|
| - int textBaseline = mTitleView.getLineBounds(mTitleView.getLineCount() - 1, null);
|
| - top = titleTop + textBaseline
|
| - + ((MarginLayoutParams) mTitleView.getLayoutParams()).bottomMargin;
|
| -
|
| - // Lay out the app rating below the title.
|
| - int starWidth = mRatingView.getMeasuredWidth();
|
| - int starTop = top + ((MarginLayoutParams) mRatingView.getLayoutParams()).topMargin;
|
| - int starBottom = starTop + mRatingView.getMeasuredHeight();
|
| - int starLeft = mIsLayoutLTR ? start : (getMeasuredWidth() - start - starWidth);
|
| - mRatingView.layout(starLeft, starTop, starLeft + starWidth, starBottom);
|
| -
|
| - // Lay out the logo in the bottom-left.
|
| - int logoWidth = mLogoView.getMeasuredWidth();
|
| - int logoBottom = bottom - ((MarginLayoutParams) mLogoView.getLayoutParams()).bottomMargin;
|
| - int logoTop = logoBottom - mLogoView.getMeasuredHeight();
|
| - int logoLeft = mIsLayoutLTR ? start : (getMeasuredWidth() - start - logoWidth);
|
| - mLogoView.layout(logoLeft, logoTop, logoLeft + logoWidth, logoBottom);
|
| -
|
| - // Lay out the install button in the bottom-right corner.
|
| - int buttonHeight = mInstallButtonView.getMeasuredHeight();
|
| - int buttonWidth = mInstallButtonView.getMeasuredWidth();
|
| - int buttonRight = mIsLayoutLTR ? end : (getMeasuredWidth() - end + buttonWidth);
|
| - int buttonLeft = buttonRight - buttonWidth;
|
| - mInstallButtonView.layout(buttonLeft, bottom - buttonHeight, buttonRight, bottom);
|
| - }
|
| -
|
| - /**
|
| - * 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 view View to measure.
|
| - * @return Measured width of the view plus its margins.
|
| - */
|
| - private static int getWidthWithMargins(View view) {
|
| - return view.getMeasuredWidth() + getMarginWidth(view);
|
| - }
|
| -
|
| - /**
|
| - * Calculates how tall the margins are for the given View.
|
| - * @param view View to measure.
|
| - * @return Measured height of the margins.
|
| - */
|
| - private static int getMarginHeight(View view) {
|
| - MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
|
| - return params.topMargin + params.bottomMargin;
|
| - }
|
| -
|
| - /**
|
| - * 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 static int getHeightWithMargins(View view) {
|
| - return view.getMeasuredHeight() + getMarginHeight(view);
|
| - }
|
| -}
|
|
|