Commit 11a2d832 authored by Sinan Sahin's avatar Sinan Sahin Committed by Commit Bot

[Messages] Update show/hide animations with specs

This CL also integrates the animations with the swipe gestures.

Bug: 1137941
Change-Id: I48ace538ebf345c4c78fc58b1de154422a416dcb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2510993Reviewed-by: default avatarLijin Shen <lazzzis@google.com>
Reviewed-by: default avatarPavel Yatsuk <pavely@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Sinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/master@{#825144}
parent d3f380f1
...@@ -39,11 +39,8 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider ...@@ -39,11 +39,8 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider
private void updateMargins() { private void updateMargins() {
CoordinatorLayout.LayoutParams params = CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mContainer.getLayoutParams(); (CoordinatorLayout.LayoutParams) mContainer.getLayoutParams();
Resources res = mContainer.getResources();
// TODO(crbug.com/1123947): Update dimens for PWAs. // TODO(crbug.com/1123947): Update dimens for PWAs.
params.topMargin = mControlsManager.getTopControlsHeight() params.topMargin = getContainerTopOffset();
- res.getDimensionPixelOffset(R.dimen.message_bubble_inset)
- res.getDimensionPixelOffset(R.dimen.message_shadow_top_margin);
mContainer.setLayoutParams(params); mContainer.setLayoutParams(params);
} }
...@@ -56,6 +53,17 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider ...@@ -56,6 +53,17 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider
mContainer.setVisibility(View.GONE); mContainer.setVisibility(View.GONE);
} }
/**
* @return The maximum translation Y value the message banner can have as a result of the
* animations or the gestures. Positive values mean the message banner can be translated
* upward from the top of the MessagesContainer.
*/
public int getMessageMaxTranslation() {
// TODO(sinansahin): We need to account for other scenarios where there are no browser
// controls visible (e.g. PWAs).
return getContainerTopOffset();
}
@Override @Override
public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset, public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) { int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) {
...@@ -66,4 +74,12 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider ...@@ -66,4 +74,12 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider
public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) { public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) {
updateMargins(); updateMargins();
} }
/** @return Offset of the message container from the top of the screen. */
private int getContainerTopOffset() {
final Resources res = mContainer.getResources();
return mControlsManager.getTopControlsHeight()
- res.getDimensionPixelOffset(R.dimen.message_bubble_inset)
- res.getDimensionPixelOffset(R.dimen.message_shadow_top_margin);
}
} }
...@@ -392,7 +392,8 @@ public class RootUiCoordinator ...@@ -392,7 +392,8 @@ public class RootUiCoordinator
MessageContainer container = mActivity.findViewById(R.id.message_container); MessageContainer container = mActivity.findViewById(R.id.message_container);
mMessageContainerCoordinator = mMessageContainerCoordinator =
new MessageContainerCoordinator(container, getBrowserControlsManager()); new MessageContainerCoordinator(container, getBrowserControlsManager());
mMessageDispatcher = MessagesFactory.createMessageDispatcher(container); mMessageDispatcher = MessagesFactory.createMessageDispatcher(
container, mMessageContainerCoordinator::getMessageMaxTranslation);
mMessageQueueMediator = new ChromeMessageQueueMediator( mMessageQueueMediator = new ChromeMessageQueueMediator(
mActivity.getBrowserControlsManager(), mMessageContainerCoordinator, mActivity.getBrowserControlsManager(), mMessageContainerCoordinator,
mActivity.getFullscreenManager(), mMessageDispatcher); mActivity.getFullscreenManager(), mMessageDispatcher);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package org.chromium.components.messages; package org.chromium.components.messages;
import org.chromium.base.supplier.Supplier;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
/** /**
...@@ -13,19 +14,24 @@ import org.chromium.ui.modelutil.PropertyModel; ...@@ -13,19 +14,24 @@ import org.chromium.ui.modelutil.PropertyModel;
public class MessageDispatcherImpl implements ManagedMessageDispatcher { public class MessageDispatcherImpl implements ManagedMessageDispatcher {
private final MessageQueueManager mMessageQueueManager = new MessageQueueManager(); private final MessageQueueManager mMessageQueueManager = new MessageQueueManager();
private final MessageContainer mMessageContainer; private final MessageContainer mMessageContainer;
private final Supplier<Integer> mMessageMaxTranslationSupplier;
/** /**
* Build a new message dispatcher * Build a new message dispatcher
* @param messageContainer A container view for displaying message banners. * @param messageContainer A container view for displaying message banners.
* @param messageMaxTranslationSupplier A {@link Supplier} that supplies the maximum translation
* Y value the message banner can have as a result of the animations or the gestures.
*/ */
public MessageDispatcherImpl(MessageContainer messageContainer) { public MessageDispatcherImpl(
MessageContainer messageContainer, Supplier<Integer> messageMaxTranslation) {
mMessageContainer = messageContainer; mMessageContainer = messageContainer;
mMessageMaxTranslationSupplier = messageMaxTranslation;
} }
@Override @Override
public void enqueueMessage(PropertyModel messageProperties) { public void enqueueMessage(PropertyModel messageProperties) {
MessageStateHandler messageStateHandler = MessageStateHandler messageStateHandler = new SingleActionMessage(mMessageContainer,
new SingleActionMessage(mMessageContainer, messageProperties, this::dismissMessage); messageProperties, this::dismissMessage, mMessageMaxTranslationSupplier);
mMessageQueueManager.enqueueMessage(messageStateHandler, messageProperties); mMessageQueueManager.enqueueMessage(messageStateHandler, messageProperties);
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package org.chromium.components.messages; package org.chromium.components.messages;
import org.chromium.base.supplier.Supplier;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
/** A factory for constructing different Messages related objects. */ /** A factory for constructing different Messages related objects. */
...@@ -11,10 +12,16 @@ public class MessagesFactory { ...@@ -11,10 +12,16 @@ public class MessagesFactory {
/** /**
* Creates an instance of ManagedMessageDispatcher. * Creates an instance of ManagedMessageDispatcher.
* @param container The MessageContainer for displaying message banners. * @param container The MessageContainer for displaying message banners.
* @param messageMaxTranslation A {@link Supplier} that supplies the maximum translation Y value
* the message banner can have as a result of the animations or the gestures, relative
* to the MessageContainer. When messages are shown, they will be animated down the
* screen, starting at the negative |messageMaxTranslation| y translation to the resting
* position in the MessageContainer.
* @return The constructed ManagedMessageDispatcher. * @return The constructed ManagedMessageDispatcher.
*/ */
public static ManagedMessageDispatcher createMessageDispatcher(MessageContainer container) { public static ManagedMessageDispatcher createMessageDispatcher(
return new MessageDispatcherImpl(container); MessageContainer container, Supplier<Integer> messageMaxTranslation) {
return new MessageDispatcherImpl(container, messageMaxTranslation);
} }
/** /**
......
...@@ -81,6 +81,9 @@ ...@@ -81,6 +81,9 @@
android:layout_weight="0" android:layout_weight="0"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minWidth="@dimen/message_banner_button_min_width" android:minWidth="@dimen/message_banner_button_min_width"
android:minHeight="@dimen/min_touch_target_size"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" /> android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/selectableItemBackground" />
</org.chromium.components.messages.MessageBannerView> </org.chromium.components.messages.MessageBannerView>
\ No newline at end of file
...@@ -18,5 +18,6 @@ ...@@ -18,5 +18,6 @@
<dimen name="message_shadow_lateral_margin">12dp</dimen> <dimen name="message_shadow_lateral_margin">12dp</dimen>
<dimen name="message_shadow_bottom_margin">16dp</dimen> <dimen name="message_shadow_bottom_margin">16dp</dimen>
<dimen name="message_bubble_inset">8dp</dimen> <dimen name="message_bubble_inset">8dp</dimen>
<dimen name="message_hide_threshold">16dp</dimen>
</resources> </resources>
\ No newline at end of file
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
package org.chromium.components.messages; package org.chromium.components.messages;
import android.content.Context; import android.content.res.Resources;
import org.chromium.base.supplier.Supplier;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor; import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
...@@ -20,11 +21,15 @@ class MessageBannerCoordinator { ...@@ -20,11 +21,15 @@ class MessageBannerCoordinator {
* *
* @param view The inflated {@link MessageBannerView}. * @param view The inflated {@link MessageBannerView}.
* @param model The model for the message banner. * @param model The model for the message banner.
* @param context The context used to get dimen resources. * @param maxTranslationSupplier A {@link Supplier} that supplies the maximum translation Y
* value the message banner can have as a result of the animations or the gestures.
* @param resources The {@link Resources}.
*/ */
MessageBannerCoordinator(MessageBannerView view, PropertyModel model, Context context) { MessageBannerCoordinator(MessageBannerView view, PropertyModel model,
Supplier<Integer> maxTranslationSupplier, Resources resources) {
PropertyModelChangeProcessor.create(model, view, MessageBannerViewBinder::bind); PropertyModelChangeProcessor.create(model, view, MessageBannerViewBinder::bind);
mMediator = new MessageBannerMediator(model, context); mMediator = new MessageBannerMediator(model, maxTranslationSupplier, resources);
view.setSwipeHandler(mMediator);
} }
/** /**
......
...@@ -9,31 +9,47 @@ import static org.chromium.components.messages.MessageBannerProperties.TRANSLATI ...@@ -9,31 +9,47 @@ import static org.chromium.components.messages.MessageBannerProperties.TRANSLATI
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.content.Context; import android.animation.TimeInterpolator;
import android.content.res.Resources;
import android.view.MotionEvent;
import org.chromium.base.MathUtils;
import org.chromium.base.supplier.Supplier;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener; import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.components.browser_ui.widget.animation.Interpolators; import org.chromium.components.browser_ui.widget.animation.Interpolators;
import org.chromium.ui.base.ViewUtils; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.ScrollDirection;
import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.SwipeHandler;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelAnimatorFactory; import org.chromium.ui.modelutil.PropertyModelAnimatorFactory;
/** /**
* Mediator responsible for the business logic in a message banner. * Mediator responsible for the business logic in a message banner.
*/ */
class MessageBannerMediator { class MessageBannerMediator implements SwipeHandler {
private static final int ANIMATION_DURATION_MS = 100; private static final int ANIMATION_DURATION_MS = 400;
private static final float ANIMATION_OFFSET_DP = 50.f; private static final TimeInterpolator TRANSLATION_SHOW_INTERPOLATOR =
Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR;
private static final TimeInterpolator TRANSLATION_HIDE_INTERPOLATOR =
Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR;
private static final TimeInterpolator ALPHA_INTERPOLATOR = Interpolators.LINEAR_INTERPOLATOR;
private PropertyModel mModel; private PropertyModel mModel;
private AnimatorSet mAnimatorSet; private AnimatorSet mAnimatorSet;
private Context mContext; private Supplier<Integer> mMaxTranslationSupplier;
private final float mHideThresholdPx;
private float mSwipeStartTranslationY;
/** /**
* Constructs the message banner mediator. * Constructs the message banner mediator.
*/ */
MessageBannerMediator(PropertyModel model, Context context) { MessageBannerMediator(
PropertyModel model, Supplier<Integer> maxTranslationSupplier, Resources resources) {
mModel = model; mModel = model;
mContext = context; mMaxTranslationSupplier = maxTranslationSupplier;
mModel.set(TRANSLATION_Y, -mMaxTranslationSupplier.get());
mHideThresholdPx = resources.getDimensionPixelSize(R.dimen.message_hide_threshold);
} }
/** /**
...@@ -41,9 +57,7 @@ class MessageBannerMediator { ...@@ -41,9 +57,7 @@ class MessageBannerMediator {
* @param messageShown The {@link Runnable} that will run once the message banner is shown. * @param messageShown The {@link Runnable} that will run once the message banner is shown.
*/ */
void show(Runnable messageShown) { void show(Runnable messageShown) {
if (mAnimatorSet != null && mAnimatorSet.isStarted()) { cancelAnyAnimations();
mAnimatorSet.cancel();
}
mAnimatorSet = createAnimatorSet(true); mAnimatorSet = createAnimatorSet(true);
mAnimatorSet.addListener(new CancelAwareAnimatorListener() { mAnimatorSet.addListener(new CancelAwareAnimatorListener() {
@Override @Override
...@@ -60,9 +74,7 @@ class MessageBannerMediator { ...@@ -60,9 +74,7 @@ class MessageBannerMediator {
* @param messageHidden The {@link Runnable} that will run once the message banner is hidden. * @param messageHidden The {@link Runnable} that will run once the message banner is hidden.
*/ */
void hide(Runnable messageHidden) { void hide(Runnable messageHidden) {
if (mAnimatorSet != null && mAnimatorSet.isStarted()) { cancelAnyAnimations();
mAnimatorSet.cancel();
}
mAnimatorSet = createAnimatorSet(false); mAnimatorSet = createAnimatorSet(false);
mAnimatorSet.addListener(new CancelAwareAnimatorListener() { mAnimatorSet.addListener(new CancelAwareAnimatorListener() {
@Override @Override
...@@ -78,21 +90,74 @@ class MessageBannerMediator { ...@@ -78,21 +90,74 @@ class MessageBannerMediator {
mModel.set(MessageBannerProperties.ON_TOUCH_RUNNABLE, runnable); mModel.set(MessageBannerProperties.ON_TOUCH_RUNNABLE, runnable);
} }
// region SwipeHandler implementation
// ---------------------------------------------------------------------------------------------
@Override
public void onSwipeStarted(@ScrollDirection int direction, MotionEvent ev) {
mSwipeStartTranslationY = mModel.get(TRANSLATION_Y);
}
@Override
public void onSwipeUpdated(
MotionEvent current, float tx, float ty, float distanceX, float distanceY) {
final float currentGesturePositionY = mSwipeStartTranslationY + ty;
final float currentTranslationY =
MathUtils.clamp(currentGesturePositionY, -mMaxTranslationSupplier.get(), 0);
mModel.set(TRANSLATION_Y, currentTranslationY);
}
// TODO(sinansahin): See if we need special handling for #onFling.
@Override
public void onSwipeFinished(MotionEvent end) {
cancelAnyAnimations();
// No need to animate if the message banner is in resting position.
if (mModel.get(TRANSLATION_Y) == 0.f) return;
mAnimatorSet = createAnimatorSet(mModel.get(TRANSLATION_Y) > -mHideThresholdPx);
mAnimatorSet.addListener(new CancelAwareAnimatorListener() {
@Override
public void onEnd(Animator animator) {
// TODO(sinansahin): We need a way to notify someone to dismiss the message once
// it's hidden.
mAnimatorSet = null;
}
});
mAnimatorSet.start();
}
@Override
public boolean isSwipeEnabled(@ScrollDirection int direction) {
// TODO(sinansahin): We will implement swiping left/right to dismiss.
return (direction == ScrollDirection.UP || direction == ScrollDirection.DOWN)
&& (mAnimatorSet == null || !mAnimatorSet.isRunning());
}
// ---------------------------------------------------------------------------------------------
// endregion
private AnimatorSet createAnimatorSet(boolean isShow) { private AnimatorSet createAnimatorSet(boolean isShow) {
final float alphaTo = isShow ? 1.f : 0.f; final float alphaTo = isShow ? 1.f : 0.f;
final Animator alphaAnimation = final Animator alphaAnimation =
PropertyModelAnimatorFactory.ofFloat(mModel, ALPHA, alphaTo); PropertyModelAnimatorFactory.ofFloat(mModel, ALPHA, alphaTo);
alphaAnimation.setInterpolator(ALPHA_INTERPOLATOR);
final float animationOffsetPx = ViewUtils.dpToPx(mContext, ANIMATION_OFFSET_DP); final float translateTo = isShow ? 0.f : -mMaxTranslationSupplier.get();
final float translateTo = isShow ? 0.f : -animationOffsetPx;
final Animator translationAnimation = final Animator translationAnimation =
PropertyModelAnimatorFactory.ofFloat(mModel, TRANSLATION_Y, translateTo); PropertyModelAnimatorFactory.ofFloat(mModel, TRANSLATION_Y, translateTo);
translationAnimation.setInterpolator(
isShow ? TRANSLATION_SHOW_INTERPOLATOR : TRANSLATION_HIDE_INTERPOLATOR);
final AnimatorSet animatorSet = new AnimatorSet(); final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(alphaAnimation).with(translationAnimation); animatorSet.play(alphaAnimation).with(translationAnimation);
animatorSet.setDuration(ANIMATION_DURATION_MS); animatorSet.setDuration(ANIMATION_DURATION_MS);
animatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
return animatorSet; return animatorSet;
} }
private void cancelAnyAnimations() {
if (mAnimatorSet != null && mAnimatorSet.isStarted()) mAnimatorSet.cancel();
mAnimatorSet = null;
}
} }
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package org.chromium.components.messages; package org.chromium.components.messages;
import static org.chromium.components.messages.MessageBannerProperties.ALPHA;
import static org.chromium.components.messages.MessageBannerProperties.DESCRIPTION; import static org.chromium.components.messages.MessageBannerProperties.DESCRIPTION;
import static org.chromium.components.messages.MessageBannerProperties.ICON; import static org.chromium.components.messages.MessageBannerProperties.ICON;
import static org.chromium.components.messages.MessageBannerProperties.ICON_RESOURCE_ID; import static org.chromium.components.messages.MessageBannerProperties.ICON_RESOURCE_ID;
...@@ -13,6 +14,7 @@ import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_B ...@@ -13,6 +14,7 @@ import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_B
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON; import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON;
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON_CONTENT_DESCRIPTION; import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON_CONTENT_DESCRIPTION;
import static org.chromium.components.messages.MessageBannerProperties.TITLE; import static org.chromium.components.messages.MessageBannerProperties.TITLE;
import static org.chromium.components.messages.MessageBannerProperties.TRANSLATION_Y;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
...@@ -54,6 +56,10 @@ public class MessageBannerViewBinder { ...@@ -54,6 +56,10 @@ public class MessageBannerViewBinder {
return false; return false;
}); });
} }
} else if (propertyKey == ALPHA) {
view.setAlpha(model.get(ALPHA));
} else if (propertyKey == TRANSLATION_Y) {
view.setTranslationY(model.get(TRANSLATION_Y));
} }
} }
} }
...@@ -12,6 +12,8 @@ import android.widget.FrameLayout; ...@@ -12,6 +12,8 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.ui.base.ViewUtils;
/** /**
* Container holding messages. * Container holding messages.
*/ */
...@@ -31,6 +33,8 @@ public class MessageContainer extends FrameLayout { ...@@ -31,6 +33,8 @@ public class MessageContainer extends FrameLayout {
"Should not contain any view when adding a new message."); "Should not contain any view when adding a new message.");
} }
addView(view); addView(view);
// TODO(sinansahin): clipChildren should be set to false only when the message is in motion.
ViewUtils.setAncestorsShouldClipChildren(this, false);
} }
/** /**
...@@ -41,6 +45,7 @@ public class MessageContainer extends FrameLayout { ...@@ -41,6 +45,7 @@ public class MessageContainer extends FrameLayout {
if (indexOfChild(view) < 0) { if (indexOfChild(view) < 0) {
throw new IllegalStateException("The given view is not being shown."); throw new IllegalStateException("The given view is not being shown.");
} }
ViewUtils.setAncestorsShouldClipChildren(this, true);
removeAllViews(); removeAllViews();
} }
} }
...@@ -11,6 +11,7 @@ import android.view.LayoutInflater; ...@@ -11,6 +11,7 @@ import android.view.LayoutInflater;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.supplier.Supplier;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
/** /**
...@@ -23,6 +24,7 @@ public class SingleActionMessage implements MessageStateHandler { ...@@ -23,6 +24,7 @@ public class SingleActionMessage implements MessageStateHandler {
private final PropertyModel mModel; private final PropertyModel mModel;
private final Callback<PropertyModel> mDismissHandler; private final Callback<PropertyModel> mDismissHandler;
private MessageAutoDismissTimer mAutoDismissTimer; private MessageAutoDismissTimer mAutoDismissTimer;
private final Supplier<Integer> mMaxTranslationSupplier;
/** /**
* @param container The container holding messages. * @param container The container holding messages.
...@@ -30,13 +32,16 @@ public class SingleActionMessage implements MessageStateHandler { ...@@ -30,13 +32,16 @@ public class SingleActionMessage implements MessageStateHandler {
* MessageBannerProperties#SINGLE_ACTION_MESSAGE_KEYS}. * MessageBannerProperties#SINGLE_ACTION_MESSAGE_KEYS}.
* @param dismissHandler The {@link Callback<PropertyModel>} able to dismiss a message by given * @param dismissHandler The {@link Callback<PropertyModel>} able to dismiss a message by given
* property model. * property model.
* @param maxTranslationSupplier A {@link Supplier} that supplies the maximum translation Y
* value the message banner can have as a result of the animations or the gestures.
*/ */
public SingleActionMessage(MessageContainer container, PropertyModel model, public SingleActionMessage(MessageContainer container, PropertyModel model,
Callback<PropertyModel> dismissHandler) { Callback<PropertyModel> dismissHandler, Supplier<Integer> maxTranslationSupplier) {
mModel = model; mModel = model;
mContainer = container; mContainer = container;
mDismissHandler = dismissHandler; mDismissHandler = dismissHandler;
mAutoDismissTimer = new MessageAutoDismissTimer(10 * DateUtils.SECOND_IN_MILLIS); mAutoDismissTimer = new MessageAutoDismissTimer(10 * DateUtils.SECOND_IN_MILLIS);
mMaxTranslationSupplier = maxTranslationSupplier;
} }
/** /**
...@@ -48,7 +53,8 @@ public class SingleActionMessage implements MessageStateHandler { ...@@ -48,7 +53,8 @@ public class SingleActionMessage implements MessageStateHandler {
if (mMessageBanner == null) { if (mMessageBanner == null) {
mView = (MessageBannerView) LayoutInflater.from(mContainer.getContext()) mView = (MessageBannerView) LayoutInflater.from(mContainer.getContext())
.inflate(R.layout.message_banner_view, mContainer, false); .inflate(R.layout.message_banner_view, mContainer, false);
mMessageBanner = new MessageBannerCoordinator(mView, mModel, mContainer.getContext()); mMessageBanner = new MessageBannerCoordinator(
mView, mModel, mMaxTranslationSupplier, mContainer.getResources());
} }
mContainer.addMessage(mView); mContainer.addMessage(mView);
mMessageBanner.show(() -> { mMessageBanner.show(() -> {
......
...@@ -50,7 +50,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase { ...@@ -50,7 +50,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase {
MessageContainer container = new MessageContainer(getActivity(), null); MessageContainer container = new MessageContainer(getActivity(), null);
PropertyModel model = createBasicSingleActionMessageModel(); PropertyModel model = createBasicSingleActionMessageModel();
SingleActionMessage message = SingleActionMessage message =
new SingleActionMessage(container, model, mEmptyDismissCallback); new SingleActionMessage(container, model, mEmptyDismissCallback, () -> 0);
final MessageBannerCoordinator messageBanner = Mockito.mock(MessageBannerCoordinator.class); final MessageBannerCoordinator messageBanner = Mockito.mock(MessageBannerCoordinator.class);
doNothing().when(messageBanner).show(any(Runnable.class)); doNothing().when(messageBanner).show(any(Runnable.class));
doNothing().when(messageBanner).setOnTouchRunnable(any(Runnable.class)); doNothing().when(messageBanner).setOnTouchRunnable(any(Runnable.class));
...@@ -81,7 +81,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase { ...@@ -81,7 +81,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase {
PropertyModel m1 = createBasicSingleActionMessageModel(); PropertyModel m1 = createBasicSingleActionMessageModel();
PropertyModel m2 = createBasicSingleActionMessageModel(); PropertyModel m2 = createBasicSingleActionMessageModel();
SingleActionMessage message1 = SingleActionMessage message1 =
new SingleActionMessage(container, m1, mEmptyDismissCallback); new SingleActionMessage(container, m1, mEmptyDismissCallback, () -> 0);
final MessageBannerCoordinator messageBanner1 = final MessageBannerCoordinator messageBanner1 =
Mockito.mock(MessageBannerCoordinator.class); Mockito.mock(MessageBannerCoordinator.class);
doNothing().when(messageBanner1).show(any(Runnable.class)); doNothing().when(messageBanner1).show(any(Runnable.class));
...@@ -89,7 +89,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase { ...@@ -89,7 +89,7 @@ public class SingleActionMessageTest extends DummyUiActivityTestCase {
message1.setMessageBannerForTesting(messageBanner1); message1.setMessageBannerForTesting(messageBanner1);
message1.setViewForTesting(view1); message1.setViewForTesting(view1);
SingleActionMessage message2 = SingleActionMessage message2 =
new SingleActionMessage(container, m2, mEmptyDismissCallback); new SingleActionMessage(container, m2, mEmptyDismissCallback, () -> 0);
final MessageBannerCoordinator messageBanner2 = final MessageBannerCoordinator messageBanner2 =
Mockito.mock(MessageBannerCoordinator.class); Mockito.mock(MessageBannerCoordinator.class);
doNothing().when(messageBanner2).show(any(Runnable.class)); doNothing().when(messageBanner2).show(any(Runnable.class));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment