Index: chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java |
index aa655442131e35e9146ed69de7663a09da07c869..17d10ffb632ac5357cd35e5a7617d2f797b5b640 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/SwipableOverlayView.java |
@@ -20,7 +20,11 @@ import android.view.ViewGroup; |
import android.view.animation.DecelerateInterpolator; |
import android.view.animation.Interpolator; |
import android.widget.FrameLayout; |
+import android.widget.ScrollView; |
+import org.chromium.chrome.browser.EmptyTabObserver; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.TabObserver; |
import org.chromium.content.browser.ContentViewCore; |
import org.chromium.content_public.browser.GestureStateListener; |
import org.chromium.ui.UiUtils; |
@@ -67,12 +71,13 @@ import org.chromium.ui.UiUtils; |
* fling is completed, the more forgiving FLING_THRESHOLD is used to determine how far a user must |
* swipe to dismiss the View rather than try to use the fling velocity. |
*/ |
-public abstract class SwipableOverlayView extends FrameLayout { |
+public abstract class SwipableOverlayView extends ScrollView { |
private static final float ALPHA_THRESHOLD = 0.25f; |
private static final float DISMISS_SWIPE_THRESHOLD = 0.75f; |
private static final float FULL_THRESHOLD = 0.5f; |
private static final float VERTICAL_FLING_SHOW_THRESHOLD = 0.2f; |
private static final float VERTICAL_FLING_HIDE_THRESHOLD = 0.9f; |
+ private static final long REATTACH_FADE_IN_MS = 250; |
protected static final float ZERO_THRESHOLD = 0.001f; |
private static final int GESTURE_NONE = 0; |
@@ -87,18 +92,34 @@ public abstract class SwipableOverlayView extends FrameLayout { |
private static final long MS_DISMISS_FLING_THRESHOLD = MS_ANIMATION_DURATION * 2; |
private static final long MS_SLOW_DISMISS = MS_ANIMATION_DURATION * 3; |
+ /** Resets the state of the SwipableOverlayView, as needed. */ |
+ protected class SwipableOverlayViewTabObserver extends EmptyTabObserver { |
+ @Override |
+ public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl, |
+ boolean isNavigationToDifferentPage, boolean isFragmentNavigation, |
+ int statusCode) { |
+ setDoStayInvisible(false); |
+ } |
+ } |
+ |
// Detects when the user is dragging the View. |
private final GestureDetector mGestureDetector; |
// Detects when the user is dragging the ContentViewCore. |
private final GestureStateListener mGestureStateListener; |
+ // Listens for changes in the layout. |
+ private final View.OnLayoutChangeListener mLayoutChangeListener; |
+ |
// Monitors for animation completions and resets the state. |
private final AnimatorListenerAdapter mAnimatorListenerAdapter; |
// Interpolator used for the animation. |
private final Interpolator mInterpolator; |
+ // Observes the Tab. |
+ private final TabObserver mTabObserver; |
+ |
// Tracks whether the user is scrolling or flinging. |
private int mGestureState; |
@@ -135,6 +156,12 @@ public abstract class SwipableOverlayView extends FrameLayout { |
// The ContentViewCore to which the overlay is added. |
private ContentViewCore mContentViewCore; |
+ // Keeps the View from becoming visible when it normally would. |
+ private boolean mDoStayInvisible; |
+ |
+ // Whether the View should be allowed to be swiped away. |
+ private boolean mIsSwipable = true; |
+ |
/** |
* Creates a SwipableOverlayView. |
* @param context Context for acquiring resources. |
@@ -146,22 +173,53 @@ public abstract class SwipableOverlayView extends FrameLayout { |
mGestureDetector = new GestureDetector(context, gestureListener); |
mGestureStateListener = createGestureStateListener(); |
mGestureState = GESTURE_NONE; |
+ mLayoutChangeListener = createLayoutChangeListener(); |
mAnimatorListenerAdapter = createAnimatorListenerAdapter(); |
mInterpolator = new DecelerateInterpolator(1.0f); |
+ mTabObserver = createTabObserver(); |
+ } |
+ |
+ /** |
+ * Indicates whether the View should be allowed to be swiped away. |
+ * @param swipable Whether the View is reacts to horizontal gestures. |
+ */ |
+ protected void setIsSwipable(boolean swipable) { |
+ mIsSwipable = swipable; |
} |
/** |
- * Adds this View to the given ContentViewCore's view. |
- * @param layout Layout to add this View to. |
+ * Watches the given ContentViewCore for scrolling changes. |
*/ |
- protected void addToView(ContentViewCore contentViewCore) { |
- assert mContentViewCore == null; |
+ public void setContentViewCore(ContentViewCore contentViewCore) { |
+ if (mContentViewCore != null) { |
+ mContentViewCore.removeGestureStateListener(mGestureStateListener); |
+ } |
+ |
mContentViewCore = contentViewCore; |
- contentViewCore.getContainerView().addView(this, 0, createLayoutParams()); |
- contentViewCore.addGestureStateListener(mGestureStateListener); |
+ if (mContentViewCore != null) { |
+ mContentViewCore.addGestureStateListener(mGestureStateListener); |
+ } |
+ } |
+ |
+ public void addToParentView(ViewGroup parentView) { |
+ if (parentView != null && parentView.indexOfChild(this) == -1) { |
+ parentView.addView(this, createLayoutParams()); |
+ |
+ // Listen for the layout to know when to animate the View coming onto the screen. |
+ addOnLayoutChangeListener(mLayoutChangeListener); |
+ } |
+ } |
+ |
+ /** |
+ * Removes the SwipableOverlayView from its parent and stops monitoring the ContentViewCore. |
+ * @return Whether the View was removed from its parent. |
+ */ |
+ public boolean removeFromParentView() { |
+ if (getParent() == null) return false; |
- // Listen for the layout to know when to animate the View coming onto the screen. |
- addOnLayoutChangeListener(createLayoutChangeListener()); |
+ ((ViewGroup) getParent()).removeView(this); |
+ removeOnLayoutChangeListener(mLayoutChangeListener); |
+ return true; |
} |
/** |
@@ -169,21 +227,43 @@ public abstract class SwipableOverlayView extends FrameLayout { |
* for other types of behavior. |
* @return LayoutParams for use when adding the View to its parent. |
*/ |
- protected ViewGroup.MarginLayoutParams createLayoutParams() { |
+ public ViewGroup.MarginLayoutParams createLayoutParams() { |
return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, |
Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); |
} |
/** |
- * Removes the View from its parent. |
+ * Call with {@code true} when a higher priority bottom element is visible to keep the View |
+ * from ever becoming visible. Call with {@code false} to restore normal visibility behavior. |
+ * @param doStayInvisible Whether the View should stay invisible even when they would |
+ * normally become visible. |
*/ |
- boolean removeFromParent() { |
- if (mContentViewCore != null) { |
- mContentViewCore.getContainerView().removeView(this); |
- mContentViewCore = null; |
- return true; |
+ public void setDoStayInvisible(boolean doStayInvisible) { |
+ mDoStayInvisible = doStayInvisible; |
+ } |
+ |
+ @Override |
+ protected void onAttachedToWindow() { |
+ super.onAttachedToWindow(); |
+ if (!mDoStayInvisible) { |
+ ObjectAnimator.ofFloat(this, "alpha", 0.f, 1.f).setDuration(REATTACH_FADE_IN_MS) |
+ .start(); |
+ setVisibility(VISIBLE); |
} |
- return false; |
+ } |
+ |
+ /** |
+ * @return TabObserver that can be used to monitor a Tab. |
+ */ |
+ protected TabObserver createTabObserver() { |
+ return new SwipableOverlayViewTabObserver(); |
+ } |
+ |
+ /** |
+ * @return TabObserver that is used to monitor a Tab. |
+ */ |
+ public TabObserver getTabObserver() { |
+ return mTabObserver; |
} |
/** |
@@ -192,8 +272,16 @@ public abstract class SwipableOverlayView extends FrameLayout { |
@Override |
protected void onLayout(boolean changed, int l, int t, int r, int b) { |
// Hide the View when the keyboard is showing. |
- boolean keyboardIsShowing = UiUtils.isKeyboardShowing(getContext(), this); |
- setVisibility(keyboardIsShowing ? INVISIBLE : VISIBLE); |
+ boolean isShowing = (getVisibility() == View.VISIBLE); |
+ if (UiUtils.isKeyboardShowing(getContext(), this)) { |
+ if (isShowing) { |
+ setVisibility(View.INVISIBLE); |
+ } |
+ } else { |
+ if (!isShowing && !mDoStayInvisible) { |
+ setVisibility(View.VISIBLE); |
+ } |
+ } |
// Update the viewport height when the parent View's height changes (e.g. after rotation). |
int currentParentHeight = getParent() == null ? 0 : ((View) getParent()).getHeight(); |
@@ -218,6 +306,8 @@ public abstract class SwipableOverlayView extends FrameLayout { |
*/ |
@Override |
public boolean onTouchEvent(MotionEvent event) { |
+ if (!mIsSwipable) return false; |
+ |
if (mGestureDetector.onTouchEvent(event)) return true; |
if (mCurrentAnimation != null) return true; |
@@ -391,7 +481,7 @@ public abstract class SwipableOverlayView extends FrameLayout { |
@Override |
public void onLayoutChange(View v, int left, int top, int right, int bottom, |
int oldLeft, int oldTop, int oldRight, int oldBottom) { |
- removeOnLayoutChangeListener(this); |
+ removeOnLayoutChangeListener(mLayoutChangeListener); |
// Animate the View coming in from the bottom of the screen. |
setTranslationY(mTotalHeight); |
@@ -560,7 +650,7 @@ public abstract class SwipableOverlayView extends FrameLayout { |
return new AnimatorListenerAdapter() { |
@Override |
public void onAnimationEnd(Animator animation) { |
- if (mIsDismissed) removeFromParent(); |
+ if (mIsDismissed) removeFromParentView(); |
mGestureState = GESTURE_NONE; |
mCurrentAnimation = null; |