| Index: chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarPopupWindow.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
|
| similarity index 39%
|
| rename from chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarPopupWindow.java
|
| rename to chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
|
| index 957a6f0cb51d7fadcaa9e41b8b98c0ba13c5b351..90bc1c62b7f5160c12dbb8c495bc4b1a3151747b 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarPopupWindow.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
|
| @@ -4,14 +4,20 @@
|
|
|
| package org.chromium.chrome.browser.snackbar;
|
|
|
| +import android.content.Context;
|
| import android.graphics.Bitmap;
|
| +import android.graphics.Rect;
|
| import android.graphics.drawable.GradientDrawable;
|
| import android.view.LayoutInflater;
|
| import android.view.View;
|
| import android.view.View.OnClickListener;
|
| import android.view.ViewGroup;
|
| +import android.view.ViewGroup.MarginLayoutParams;
|
| +import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
| +import android.view.animation.Animation;
|
| +import android.view.animation.AnimationUtils;
|
| +import android.view.animation.DecelerateInterpolator;
|
| import android.widget.ImageView;
|
| -import android.widget.PopupWindow;
|
| import android.widget.TextView;
|
|
|
| import org.chromium.base.ApiCompatibilityUtils;
|
| @@ -19,61 +25,134 @@ import org.chromium.chrome.R;
|
| import org.chromium.ui.base.DeviceFormFactor;
|
|
|
| /**
|
| - * Visual representation of a snackbar. On phone it fills the width of the activity; on tablet it
|
| + * Visual representation of a snackbar. On phone it matches the width of the activity; on tablet it
|
| * has a fixed width and is anchored at the start-bottom corner of the current window.
|
| */
|
| -class SnackbarPopupWindow extends PopupWindow {
|
| +class SnackbarView {
|
| + private final ViewGroup mView;
|
| + private final ViewGroup mParent;
|
| private final TemplatePreservingTextView mMessageView;
|
| private final TextView mActionButtonView;
|
| private final ImageView mProfileImageView;
|
| private final int mAnimationDuration;
|
| + private final boolean mIsTablet;
|
| private Snackbar mSnackbar;
|
|
|
| + // Variables used to calculate the virtual keyboard's height.
|
| + private int[] mTempDecorPosition = new int[2];
|
| + private Rect mCurrentVisibleRect = new Rect();
|
| + private Rect mPreviousVisibleRect = new Rect();
|
| +
|
| + private OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() {
|
| + @Override
|
| + public void onGlobalLayout() {
|
| + // Why adjust layout params in onGlobalLayout, not in onMeasure()?
|
| + // View#onMeasure() does not work well with getWindowVisibleDisplayFrame(). Especially
|
| + // when device orientation changes, onMeasure is called before
|
| + // getWindowVisibleDisplayFrame's value is updated.
|
| + adjustViewPosition();
|
| + }
|
| + };
|
| +
|
| /**
|
| - * Creates an instance of the {@link SnackbarPopupWindow}.
|
| - * @param parent Parent View the popup window anchors to
|
| + * Creates an instance of the {@link SnackbarView}.
|
| + * @param parent The main view of the embedding Activity.
|
| * @param listener An {@link OnClickListener} that will be called when the action button is
|
| * clicked.
|
| * @param snackbar The snackbar to be displayed.
|
| */
|
| - SnackbarPopupWindow(View parent, OnClickListener listener, Snackbar snackbar) {
|
| - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.snackbar, null);
|
| - setContentView(view);
|
| - mMessageView = (TemplatePreservingTextView) view.findViewById(R.id.snackbar_message);
|
| - mActionButtonView = (TextView) view.findViewById(R.id.snackbar_button);
|
| - mAnimationDuration = view.getResources().getInteger(
|
| - android.R.integer.config_mediumAnimTime);
|
| + SnackbarView(ViewGroup parent, OnClickListener listener, Snackbar snackbar) {
|
| + Context context = parent.getContext();
|
| + mIsTablet = DeviceFormFactor.isTablet(context);
|
| + mParent = parent;
|
| + mView = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.snackbar, mParent, false);
|
| + mAnimationDuration = mView.getResources()
|
| + .getInteger(android.R.integer.config_mediumAnimTime);
|
| + mMessageView = (TemplatePreservingTextView) mView.findViewById(R.id.snackbar_message);
|
| + mActionButtonView = (TextView) mView.findViewById(R.id.snackbar_button);
|
| mActionButtonView.setOnClickListener(listener);
|
| - mProfileImageView = (ImageView) view.findViewById(R.id.snackbar_profile_image);
|
| + mProfileImageView = (ImageView) mView.findViewById(R.id.snackbar_profile_image);
|
|
|
| - // Set width and height of popup window
|
| - boolean isTablet = DeviceFormFactor.isTablet(parent.getContext());
|
| - setWidth(isTablet
|
| - ? parent.getResources().getDimensionPixelSize(R.dimen.snackbar_tablet_width)
|
| - : parent.getWidth());
|
| + updateInternal(snackbar, false);
|
| + }
|
|
|
| - setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
| - update(snackbar, false);
|
| + void show() {
|
| + mParent.addView(mView);
|
| + adjustViewPosition();
|
| + mView.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutListener);
|
| +
|
| + // Animation, instead of Animator or ViewPropertyAnimator is prefered here because:
|
| + // 1. Animation xml allows specifying 100% as translation, which is much more convenient
|
| + // than waiting for mView to be laid out to get measured height.
|
| + // 2. Animation avoids SurfaceView's clipping issue when doing translating animation.
|
| + Animation fadeIn = AnimationUtils.loadAnimation(mParent.getContext(), R.anim.snackbar_in);
|
| + mView.startAnimation(fadeIn);
|
| }
|
|
|
| - @Override
|
| - public void dismiss() {
|
| + void dismiss() {
|
| // Disable action button during animation.
|
| mActionButtonView.setEnabled(false);
|
| - super.dismiss();
|
| + mView.getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutListener);
|
| + // ViewPropertyAnimator is prefered here because Animation is not canceled when the activity
|
| + // is in backbround.
|
| + mView.animate()
|
| + .alpha(0f)
|
| + .setDuration(mAnimationDuration)
|
| + .setInterpolator(new DecelerateInterpolator())
|
| + .withEndAction(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mParent.removeView(mView);
|
| + }
|
| + }).start();
|
| + }
|
| +
|
| + void adjustViewPosition() {
|
| + mParent.getWindowVisibleDisplayFrame(mCurrentVisibleRect);
|
| + // Only update if the visible frame has changed, otherwise there will be a layout loop.
|
| + if (!mCurrentVisibleRect.equals(mPreviousVisibleRect)) {
|
| + mPreviousVisibleRect.set(mCurrentVisibleRect);
|
| +
|
| + mParent.getLocationInWindow(mTempDecorPosition);
|
| + int activityHeight = mTempDecorPosition[1] + mParent.getHeight();
|
| + int visibleHeight = Math.min(mCurrentVisibleRect.bottom, activityHeight);
|
| + int keyboardHeight = activityHeight - visibleHeight;
|
| +
|
| + int margin = mParent.getResources().getDimensionPixelSize(R.dimen.snackbar_margin);
|
| + MarginLayoutParams lp = (MarginLayoutParams) mView.getLayoutParams();
|
| + lp.bottomMargin = keyboardHeight;
|
| + if (mIsTablet) {
|
| + lp.bottomMargin += margin;
|
| + int width = mParent.getResources().getDimensionPixelSize(R.dimen.snackbar_width);
|
| + lp.width = Math.min(width, mParent.getWidth() - 2 * margin);
|
| + }
|
| + mView.setLayoutParams(lp);
|
| + }
|
| + }
|
| +
|
| + boolean isShowing() {
|
| + return mView.isShown();
|
| + }
|
| +
|
| + /**
|
| + * Sends an accessibility event to mMessageView announcing that this window was added so that
|
| + * the mMessageView content description is read aloud if accessibility is enabled.
|
| + */
|
| + void announceforAccessibility() {
|
| + mMessageView.announceForAccessibility(mMessageView.getContentDescription());
|
| }
|
|
|
| /**
|
| - * Updates the view to display data from the given snackbar. No-op if the popup is already
|
| + * Updates the view to display data from the given snackbar. No-op if the view is already
|
| * showing the given snackbar.
|
| * @param snackbar The snackbar to display
|
| * @return Whether update has actually been executed.
|
| */
|
| boolean update(Snackbar snackbar) {
|
| - return update(snackbar, true);
|
| + return updateInternal(snackbar, true);
|
| }
|
|
|
| - private boolean update(Snackbar snackbar, boolean animate) {
|
| + private boolean updateInternal(Snackbar snackbar, boolean animate) {
|
| if (mSnackbar == snackbar) return false;
|
| mSnackbar = snackbar;
|
| mMessageView.setMaxLines(snackbar.getSingleLine() ? 1 : Integer.MAX_VALUE);
|
| @@ -81,23 +160,22 @@ class SnackbarPopupWindow extends PopupWindow {
|
| setViewText(mMessageView, snackbar.getText(), animate);
|
| String actionText = snackbar.getActionText();
|
|
|
| - View view = getContentView();
|
| int backgroundColor = snackbar.getBackgroundColor();
|
| if (backgroundColor == 0) {
|
| - backgroundColor = ApiCompatibilityUtils.getColor(view.getResources(),
|
| + backgroundColor = ApiCompatibilityUtils.getColor(mView.getResources(),
|
| R.color.snackbar_background_color);
|
| }
|
|
|
| - if (DeviceFormFactor.isTablet(view.getContext())) {
|
| - // On tablet, snackbar popups have rounded corners.
|
| - view.setBackgroundResource(R.drawable.snackbar_background);
|
| - ((GradientDrawable) view.getBackground()).setColor(backgroundColor);
|
| + if (mIsTablet) {
|
| + // On tablet, snackbars have rounded corners.
|
| + mView.setBackgroundResource(R.drawable.snackbar_background);
|
| + ((GradientDrawable) mView.getBackground()).setColor(backgroundColor);
|
| } else {
|
| - view.setBackgroundColor(backgroundColor);
|
| + mView.setBackgroundColor(backgroundColor);
|
| }
|
|
|
| if (snackbar.getBackgroundColor() != 0) {
|
| - view.setBackgroundColor(snackbar.getBackgroundColor());
|
| + mView.setBackgroundColor(snackbar.getBackgroundColor());
|
| }
|
| if (actionText != null) {
|
| mActionButtonView.setVisibility(View.VISIBLE);
|
| @@ -109,7 +187,7 @@ class SnackbarPopupWindow extends PopupWindow {
|
| if (profileImage != null) {
|
| mProfileImageView.setImageBitmap(profileImage);
|
| } else {
|
| - ((ViewGroup) view).removeView(mProfileImageView);
|
| + mView.removeView(mProfileImageView);
|
| }
|
| return true;
|
| }
|
| @@ -125,12 +203,4 @@ class SnackbarPopupWindow extends PopupWindow {
|
| view.setText(text);
|
| }
|
| }
|
| -
|
| - /**
|
| - * Sends an accessibility event to mMessageView announcing that this window was added so that
|
| - * the mMessageView content description is read aloud if accessibility is enabled.
|
| - */
|
| - void announceforAccessibility() {
|
| - mMessageView.announceForAccessibility(mMessageView.getContentDescription());
|
| - }
|
| }
|
|
|