| Index: chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
|
| index c858cab44dfa7b5ff13777584f56fd54d584ee75..0475fd92a5bf7acc256cb9355784e408b3a9bdf9 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
|
| @@ -4,20 +4,13 @@
|
|
|
| package org.chromium.chrome.browser.snackbar;
|
|
|
| -import android.graphics.Rect;
|
| import android.os.Handler;
|
| -import android.view.Gravity;
|
| import android.view.View;
|
| import android.view.View.OnClickListener;
|
| -import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
| -import android.view.Window;
|
| +import android.view.ViewGroup;
|
|
|
| -import org.chromium.base.ApiCompatibilityUtils;
|
| import org.chromium.base.VisibleForTesting;
|
| -import org.chromium.chrome.R;
|
| import org.chromium.chrome.browser.device.DeviceClassManager;
|
| -import org.chromium.ui.UiUtils;
|
| -import org.chromium.ui.base.DeviceFormFactor;
|
|
|
| /**
|
| * Manager for the snackbar showing at the bottom of activity. There should be only one
|
| @@ -28,7 +21,7 @@ import org.chromium.ui.base.DeviceFormFactor;
|
| * during {@link #DEFAULT_SNACKBAR_DURATION_MS} milliseconds, it will call
|
| * {@link SnackbarController#onDismissNoAction(Object)}.
|
| */
|
| -public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener {
|
| +public class SnackbarManager implements OnClickListener {
|
|
|
| /**
|
| * Interface that shows the ability to provide a snackbar manager. Activities implementing this
|
| @@ -77,32 +70,27 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS;
|
| private static int sAccessibilitySnackbarDurationMs = ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS;
|
|
|
| - private final boolean mIsTablet;
|
| -
|
| - private View mDecor;
|
| + private ViewGroup mParentView;
|
| + private SnackbarView mView;
|
| private final Handler mUIThreadHandler;
|
| private SnackbarCollection mSnackbars = new SnackbarCollection();
|
| - private SnackbarPopupWindow mPopup;
|
| private boolean mActivityInForeground;
|
| + private boolean mIsDisabledForTesting;
|
| private final Runnable mHideRunnable = new Runnable() {
|
| @Override
|
| public void run() {
|
| mSnackbars.removeCurrentDueToTimeout();
|
| - updatePopup();
|
| + updateView();
|
| }
|
| };
|
|
|
| - // Variables used and reused in popup position calculations.
|
| - private int[] mTempDecorPosition = new int[2];
|
| - private Rect mTempVisibleDisplayFrame = new Rect();
|
| -
|
| /**
|
| * Constructs a SnackbarManager to show snackbars in the given window.
|
| + * @param rootView The main view (e.g. android.R.id.content) of the embedding activity.
|
| */
|
| - public SnackbarManager(Window window) {
|
| - mDecor = window.getDecorView();
|
| + public SnackbarManager(ViewGroup rootView) {
|
| + mParentView = rootView;
|
| mUIThreadHandler = new Handler();
|
| - mIsTablet = DeviceFormFactor.isTablet(mDecor.getContext());
|
| }
|
|
|
| /**
|
| @@ -117,7 +105,7 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| */
|
| public void onStop() {
|
| mSnackbars.clear();
|
| - updatePopup();
|
| + updateView();
|
| mActivityInForeground = false;
|
| }
|
|
|
| @@ -126,10 +114,10 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| * visible.
|
| */
|
| public void showSnackbar(Snackbar snackbar) {
|
| - if (!mActivityInForeground) return;
|
| + if (!mActivityInForeground || mIsDisabledForTesting) return;
|
| mSnackbars.add(snackbar);
|
| - updatePopup();
|
| - mPopup.announceforAccessibility();
|
| + updateView();
|
| + mView.announceforAccessibility();
|
| }
|
|
|
| /**
|
| @@ -139,7 +127,7 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| */
|
| public void dismissSnackbars(SnackbarController controller) {
|
| if (mSnackbars.removeMatchingSnackbars(controller)) {
|
| - updatePopup();
|
| + updateView();
|
| }
|
| }
|
|
|
| @@ -151,7 +139,7 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| */
|
| public void dismissSnackbars(SnackbarController controller, Object actionData) {
|
| if (mSnackbars.removeMatchingSnackbars(controller, actionData)) {
|
| - updatePopup();
|
| + updateView();
|
| }
|
| }
|
|
|
| @@ -162,7 +150,7 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| */
|
| public void dismissSnackbars(SnackbarController controller, ActionDataMatcher selector) {
|
| if (mSnackbars.removeMatchingSnackbars(controller, selector)) {
|
| - updatePopup();
|
| + updateView();
|
| }
|
| }
|
|
|
| @@ -172,87 +160,43 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| @Override
|
| public void onClick(View v) {
|
| mSnackbars.removeCurrentDueToAction();
|
| - updatePopup();
|
| + updateView();
|
| }
|
|
|
| /**
|
| * @return Whether there is a snackbar on screen.
|
| */
|
| public boolean isShowing() {
|
| - return mPopup != null && mPopup.isShowing();
|
| + return mView != null && mView.isShowing();
|
| }
|
|
|
| /**
|
| - * Resize and re-position popup window when the device orientation changes or the software
|
| - * keyboard appears. Be careful not to let the snackbar overlap the Android navigation bar:
|
| - * http://b/17789629.
|
| + * Updates the {@link SnackbarView} to reflect the value of mSnackbars.currentSnackbar(), which
|
| + * may be null. This might show, change, or hide the view.
|
| */
|
| - @Override
|
| - public void onGlobalLayout() {
|
| - if (mPopup == null) return;
|
| -
|
| - mDecor.getLocationInWindow(mTempDecorPosition);
|
| - mDecor.getWindowVisibleDisplayFrame(mTempVisibleDisplayFrame);
|
| - int decorBottom = mTempDecorPosition[1] + mDecor.getHeight();
|
| - int visibleBottom = Math.min(mTempVisibleDisplayFrame.bottom, decorBottom);
|
| -
|
| - if (mIsTablet) {
|
| - int margin = mDecor.getResources().getDimensionPixelOffset(
|
| - R.dimen.snackbar_tablet_margin);
|
| - int width = mDecor.getResources().getDimensionPixelSize(R.dimen.snackbar_tablet_width);
|
| - boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(mDecor);
|
| - int startPosition = isRtl ? mDecor.getRight() - width - margin
|
| - : mDecor.getLeft() + margin;
|
| - mPopup.update(startPosition, decorBottom - visibleBottom + margin, width, -1);
|
| - } else {
|
| - mPopup.update(mDecor.getLeft(), decorBottom - visibleBottom, mDecor.getWidth(), -1);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Updates the snackbar popup window to reflect the value of mSnackbars.currentSnackbar(), which
|
| - * may be null. This might show, change, or hide the popup.
|
| - */
|
| - private void updatePopup() {
|
| + private void updateView() {
|
| if (!mActivityInForeground) return;
|
| Snackbar currentSnackbar = mSnackbars.getCurrent();
|
| if (currentSnackbar == null) {
|
| mUIThreadHandler.removeCallbacks(mHideRunnable);
|
| - if (mPopup != null) {
|
| - mPopup.dismiss();
|
| - mPopup = null;
|
| + if (mView != null) {
|
| + mView.dismiss();
|
| + mView = null;
|
| }
|
| - mDecor.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
| } else {
|
| - boolean popupChanged = true;
|
| - if (mPopup == null) {
|
| - mPopup = new SnackbarPopupWindow(mDecor, this, currentSnackbar);
|
| - // When the keyboard is showing, translating the snackbar upwards looks bad because
|
| - // it overlaps the keyboard. In this case, use an alternative animation without
|
| - // translation.
|
| - boolean isKeyboardShowing = UiUtils.isKeyboardShowing(mDecor.getContext(), mDecor);
|
| - mPopup.setAnimationStyle(isKeyboardShowing ? R.style.SnackbarAnimationWithKeyboard
|
| - : R.style.SnackbarAnimation);
|
| -
|
| - mDecor.getLocationInWindow(mTempDecorPosition);
|
| - mDecor.getWindowVisibleDisplayFrame(mTempVisibleDisplayFrame);
|
| - int decorBottom = mTempDecorPosition[1] + mDecor.getHeight();
|
| - int visibleBottom = Math.min(mTempVisibleDisplayFrame.bottom, decorBottom);
|
| - int margin = mIsTablet ? mDecor.getResources().getDimensionPixelSize(
|
| - R.dimen.snackbar_tablet_margin) : 0;
|
| -
|
| - mPopup.showAtLocation(mDecor, Gravity.START | Gravity.BOTTOM, margin,
|
| - decorBottom - visibleBottom + margin);
|
| - mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
| + boolean viewChanged = true;
|
| + if (mView == null) {
|
| + mView = new SnackbarView(mParentView, this, currentSnackbar);
|
| + mView.show();
|
| } else {
|
| - popupChanged = mPopup.update(currentSnackbar);
|
| + viewChanged = mView.update(currentSnackbar);
|
| }
|
|
|
| - if (popupChanged) {
|
| + if (viewChanged) {
|
| int durationMs = getDuration(currentSnackbar);
|
| mUIThreadHandler.removeCallbacks(mHideRunnable);
|
| mUIThreadHandler.postDelayed(mHideRunnable, durationMs);
|
| - mPopup.announceforAccessibility();
|
| + mView.announceforAccessibility();
|
| }
|
| }
|
|
|
| @@ -261,13 +205,21 @@ public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
|
| private int getDuration(Snackbar snackbar) {
|
| int durationMs = snackbar.getDuration();
|
| if (durationMs == 0) {
|
| - durationMs = DeviceClassManager.isAccessibilityModeEnabled(mDecor.getContext())
|
| + durationMs = DeviceClassManager.isAccessibilityModeEnabled(mParentView.getContext())
|
| ? sAccessibilitySnackbarDurationMs : sSnackbarDurationMs;
|
| }
|
| return durationMs;
|
| }
|
|
|
| /**
|
| + * Disables the snackbar manager. This is only intented for testing purposes.
|
| + */
|
| + @VisibleForTesting
|
| + public void disableForTesting() {
|
| + mIsDisabledForTesting = true;
|
| + }
|
| +
|
| + /**
|
| * Overrides the default snackbar duration with a custom value for testing.
|
| * @param durationMs The duration to use in ms.
|
| */
|
|
|