Index: chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java |
index f30964fb53febd22e7f2439bf5cd9f15a0a374d0..008f269a6f5970a15d8c310513015c67c892da00 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java |
@@ -4,24 +4,32 @@ |
package org.chromium.chrome.browser.snackbar; |
-import android.content.Context; |
+import android.animation.Animator; |
+import android.animation.AnimatorListenerAdapter; |
+import android.animation.ObjectAnimator; |
+import android.app.Activity; |
import android.graphics.Bitmap; |
import android.graphics.Rect; |
import android.graphics.drawable.GradientDrawable; |
+import android.support.design.widget.CoordinatorLayout; |
+import android.support.design.widget.CoordinatorLayout.Behavior; |
+import android.support.design.widget.CoordinatorLayout.LayoutParams; |
import android.view.LayoutInflater; |
+import android.view.MotionEvent; |
+import android.view.SurfaceView; |
import android.view.View; |
import android.view.View.OnClickListener; |
+import android.view.View.OnLayoutChangeListener; |
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.TextView; |
import org.chromium.base.ApiCompatibilityUtils; |
import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.ChromeActivity; |
import org.chromium.ui.base.DeviceFormFactor; |
/** |
@@ -29,13 +37,14 @@ import org.chromium.ui.base.DeviceFormFactor; |
* has a fixed width and is anchored at the start-bottom corner of the current window. |
*/ |
class SnackbarView { |
+ private final Activity mActivity; |
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 ViewGroup mParent; |
private Snackbar mSnackbar; |
// Variables used to calculate the virtual keyboard's height. |
@@ -55,17 +64,37 @@ class SnackbarView { |
}; |
/** |
+ * Behavior that intercepts touch event from the CompositorViewHoder. |
+ */ |
+ private final Behavior<View> mBehavior = new Behavior<View>() { |
+ @Override |
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) { |
+ return ev.getX() - child.getX() < child.getWidth() |
+ && ev.getY() - child.getY() < child.getHeight(); |
+ } |
+ |
+ @Override |
+ public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) { |
+ ev.offsetLocation(-child.getX(), -child.getY()); |
+ boolean consumed = child.dispatchTouchEvent(ev); |
+ ev.offsetLocation(child.getX(), child.getY()); |
+ return consumed; |
+ } |
+ }; |
+ |
+ /** |
* Creates an instance of the {@link SnackbarView}. |
- * @param parent The main view of the embedding Activity. |
+ * @param activity The activity that displays the snackbar. |
* @param listener An {@link OnClickListener} that will be called when the action button is |
* clicked. |
* @param snackbar The snackbar to be displayed. |
*/ |
- 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); |
+ SnackbarView(Activity activity, OnClickListener listener, Snackbar snackbar) { |
+ mActivity = activity; |
+ mIsTablet = DeviceFormFactor.isTablet(activity); |
+ mParent = findParentView(activity); |
+ mView = (ViewGroup) LayoutInflater.from(activity).inflate( |
+ R.layout.snackbar, mParent, false); |
mAnimationDuration = mView.getResources() |
.getInteger(android.R.integer.config_mediumAnimTime); |
mMessageView = (TemplatePreservingTextView) mView.findViewById(R.id.snackbar_message); |
@@ -77,34 +106,45 @@ class SnackbarView { |
} |
void show() { |
- mParent.addView(mView); |
+ if (mParent instanceof CoordinatorLayout) { |
+ CoordinatorLayout.LayoutParams lp = (LayoutParams) mView.getLayoutParams(); |
+ lp.setBehavior(mBehavior); |
+ mParent.addView(mView, lp); |
+ } else { |
+ 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); |
+ mView.addOnLayoutChangeListener(new OnLayoutChangeListener() { |
+ @Override |
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, |
+ int oldLeft, int oldTop, int oldRight, int oldBottom) { |
+ mView.removeOnLayoutChangeListener(this); |
+ mView.setTranslationY(mView.getHeight()); |
+ Animator animator = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, 0); |
+ animator.setInterpolator(new DecelerateInterpolator()); |
+ animator.setDuration(mAnimationDuration); |
+ startAnimatorOnSurfaceView(animator); |
+ } |
+ }); |
} |
void dismiss() { |
// Disable action button during animation. |
mActionButtonView.setEnabled(false); |
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(); |
+ |
+ Animator animator = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, mView.getHeight()); |
+ animator.setInterpolator(new DecelerateInterpolator()); |
+ animator.setDuration(mAnimationDuration); |
+ animator.addListener(new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ mParent.removeView(mView); |
+ } |
+ }); |
+ startAnimatorOnSurfaceView(animator); |
} |
void adjustViewPosition() { |
@@ -195,6 +235,30 @@ class SnackbarView { |
return true; |
} |
+ /** |
+ * @return The parent {@link ViewGroup} that {@link #mView} will be added to. |
+ */ |
+ private ViewGroup findParentView(Activity activity) { |
+ if (activity instanceof ChromeActivity) { |
+ return ((ChromeActivity) activity).getCompositorViewHolder(); |
+ } else { |
+ return (ViewGroup) activity.findViewById(android.R.id.content); |
+ } |
+ } |
+ |
+ /** |
+ * Starts the {@link Animator} with {@link SurfaceView} optimization disabled. If a |
+ * {@link SurfaceView} is not present in the given {@link Activity}, start the {@link Animator} |
+ * in the normal way. |
+ */ |
+ private void startAnimatorOnSurfaceView(Animator animator) { |
+ if (mActivity instanceof ChromeActivity) { |
+ ((ChromeActivity) mActivity).getWindowAndroid().startAnimationOverContent(animator); |
+ } else { |
+ animator.start(); |
+ } |
+ } |
+ |
private void setViewText(TextView view, CharSequence text, boolean animate) { |
if (view.getText().toString().equals(text)) return; |
view.animate().cancel(); |