Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3446)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java

Issue 1759483002: [Snackbar] Replace popup window with views (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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());
- }
}

Powered by Google App Engine
This is Rietveld 408576698