Merge changes from topic "b215583339-AnimBigPic" into tm-qpr-dev
* changes: Animates drawables in Notifications Adds flag for Animated Big Picture notifications
This commit is contained in:
committed by
Android (Google) Code Review
commit
7014088025
@@ -102,6 +102,10 @@ object Flags {
|
|||||||
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
|
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
|
||||||
unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
|
unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
|
||||||
|
|
||||||
|
// TODO(b/263414400): Tracking Bug
|
||||||
|
@JvmField
|
||||||
|
val NOTIFICATION_ANIMATE_BIG_PICTURE = unreleasedFlag(120, "notification_animate_big_picture")
|
||||||
|
|
||||||
// 200 - keyguard/lockscreen
|
// 200 - keyguard/lockscreen
|
||||||
// ** Flag retired **
|
// ** Flag retired **
|
||||||
// public static final BooleanFlag KEYGUARD_LAYOUT =
|
// public static final BooleanFlag KEYGUARD_LAYOUT =
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ import com.android.internal.widget.CallLayout;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.animation.Interpolators;
|
import com.android.systemui.animation.Interpolators;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
@@ -177,6 +179,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
|
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
|
||||||
private Optional<BubblesManager> mBubblesManagerOptional;
|
private Optional<BubblesManager> mBubblesManagerOptional;
|
||||||
private MetricsLogger mMetricsLogger;
|
private MetricsLogger mMetricsLogger;
|
||||||
|
private FeatureFlags mFeatureFlags;
|
||||||
private int mIconTransformContentShift;
|
private int mIconTransformContentShift;
|
||||||
private int mMaxHeadsUpHeightBeforeN;
|
private int mMaxHeadsUpHeightBeforeN;
|
||||||
private int mMaxHeadsUpHeightBeforeP;
|
private int mMaxHeadsUpHeightBeforeP;
|
||||||
@@ -277,7 +280,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
private boolean mChildIsExpanding;
|
private boolean mChildIsExpanding;
|
||||||
|
|
||||||
private boolean mJustClicked;
|
private boolean mJustClicked;
|
||||||
private boolean mIconAnimationRunning;
|
private boolean mAnimationRunning;
|
||||||
private boolean mShowNoBackground;
|
private boolean mShowNoBackground;
|
||||||
private ExpandableNotificationRow mNotificationParent;
|
private ExpandableNotificationRow mNotificationParent;
|
||||||
private OnExpandClickListener mOnExpandClickListener;
|
private OnExpandClickListener mOnExpandClickListener;
|
||||||
@@ -451,10 +454,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
return mPublicLayout;
|
return mPublicLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIconAnimationRunning(boolean running) {
|
/**
|
||||||
for (NotificationContentView l : mLayouts) {
|
* Sets animations running in the layouts of this row, including public, private, and children.
|
||||||
setIconAnimationRunning(running, l);
|
* @param running whether the animations should be started running or stopped.
|
||||||
|
*/
|
||||||
|
public void setAnimationRunning(boolean running) {
|
||||||
|
// Sets animations running in the private/public layouts.
|
||||||
|
if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE)) {
|
||||||
|
for (NotificationContentView l : mLayouts) {
|
||||||
|
if (l != null) {
|
||||||
|
l.setContentAnimationRunning(running);
|
||||||
|
setIconAnimationRunning(running, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (NotificationContentView l : mLayouts) {
|
||||||
|
setIconAnimationRunning(running, l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// For groups summaries with children, we want to set the children containers
|
||||||
|
// animating as well.
|
||||||
if (mIsSummaryWithChildren) {
|
if (mIsSummaryWithChildren) {
|
||||||
NotificationViewWrapper viewWrapper = mChildrenContainer.getNotificationViewWrapper();
|
NotificationViewWrapper viewWrapper = mChildrenContainer.getNotificationViewWrapper();
|
||||||
if (viewWrapper != null) {
|
if (viewWrapper != null) {
|
||||||
@@ -468,12 +487,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mChildrenContainer.getAttachedChildren();
|
mChildrenContainer.getAttachedChildren();
|
||||||
for (int i = 0; i < notificationChildren.size(); i++) {
|
for (int i = 0; i < notificationChildren.size(); i++) {
|
||||||
ExpandableNotificationRow child = notificationChildren.get(i);
|
ExpandableNotificationRow child = notificationChildren.get(i);
|
||||||
child.setIconAnimationRunning(running);
|
child.setAnimationRunning(running);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mIconAnimationRunning = running;
|
mAnimationRunning = running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or stops animations of the icons in all potential content views (regardless of
|
||||||
|
* whether they're contracted, expanded, etc).
|
||||||
|
*
|
||||||
|
* @param running whether to start or stop the icon's animation.
|
||||||
|
*/
|
||||||
private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
|
private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
|
||||||
if (layout != null) {
|
if (layout != null) {
|
||||||
View contractedChild = layout.getContractedChild();
|
View contractedChild = layout.getContractedChild();
|
||||||
@@ -485,16 +510,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or stops animations of the icon in the provided view's icon and right icon.
|
||||||
|
*
|
||||||
|
* @param running whether to start or stop the icon's animation.
|
||||||
|
* @param child the view with the icon to start or stop.
|
||||||
|
*/
|
||||||
private void setIconAnimationRunningForChild(boolean running, View child) {
|
private void setIconAnimationRunningForChild(boolean running, View child) {
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
ImageView icon = child.findViewById(com.android.internal.R.id.icon);
|
ImageView icon = child.findViewById(com.android.internal.R.id.icon);
|
||||||
setIconRunning(icon, running);
|
setImageViewAnimationRunning(icon, running);
|
||||||
ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
|
ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
|
||||||
setIconRunning(rightIcon, running);
|
setImageViewAnimationRunning(rightIcon, running);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setIconRunning(ImageView imageView, boolean running) {
|
/**
|
||||||
|
* Starts or stops the animation of a provided image view if it's an AnimationDrawable or an
|
||||||
|
* AnimatedVectorDrawable.
|
||||||
|
*
|
||||||
|
* @param imageView the image view on which to start/stop animation.
|
||||||
|
* @param running whether to start or stop the view's animation.
|
||||||
|
*/
|
||||||
|
private void setImageViewAnimationRunning(ImageView imageView, boolean running) {
|
||||||
if (imageView != null) {
|
if (imageView != null) {
|
||||||
Drawable drawable = imageView.getDrawable();
|
Drawable drawable = imageView.getDrawable();
|
||||||
if (drawable instanceof AnimationDrawable) {
|
if (drawable instanceof AnimationDrawable) {
|
||||||
@@ -561,8 +599,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
|
mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
|
||||||
mChildrenContainer.onNotificationUpdated();
|
mChildrenContainer.onNotificationUpdated();
|
||||||
}
|
}
|
||||||
if (mIconAnimationRunning) {
|
if (mAnimationRunning) {
|
||||||
setIconAnimationRunning(true);
|
setAnimationRunning(true);
|
||||||
}
|
}
|
||||||
if (mLastChronometerRunning) {
|
if (mLastChronometerRunning) {
|
||||||
setChronometerRunning(true);
|
setChronometerRunning(true);
|
||||||
@@ -1038,7 +1076,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
notifyHeightChanged(false /* needsAnimation */);
|
notifyHeightChanged(false /* needsAnimation */);
|
||||||
}
|
}
|
||||||
if (pinned) {
|
if (pinned) {
|
||||||
setIconAnimationRunning(true);
|
setAnimationRunning(true);
|
||||||
mExpandedWhenPinned = false;
|
mExpandedWhenPinned = false;
|
||||||
} else if (mExpandedWhenPinned) {
|
} else if (mExpandedWhenPinned) {
|
||||||
setUserExpanded(true);
|
setUserExpanded(true);
|
||||||
@@ -1627,6 +1665,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an ExpandableNotificationRow.
|
||||||
|
* @param context context passed to image resolver
|
||||||
|
* @param attrs attributes used to initialize parent view
|
||||||
|
*/
|
||||||
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
mImageResolver = new NotificationInlineImageResolver(context,
|
mImageResolver = new NotificationInlineImageResolver(context,
|
||||||
@@ -1662,7 +1705,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
NotificationGutsManager gutsManager,
|
NotificationGutsManager gutsManager,
|
||||||
MetricsLogger metricsLogger,
|
MetricsLogger metricsLogger,
|
||||||
SmartReplyConstants smartReplyConstants,
|
SmartReplyConstants smartReplyConstants,
|
||||||
SmartReplyController smartReplyController) {
|
SmartReplyController smartReplyController,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
mEntry = entry;
|
mEntry = entry;
|
||||||
mAppName = appName;
|
mAppName = appName;
|
||||||
if (mMenuRow == null) {
|
if (mMenuRow == null) {
|
||||||
@@ -1697,6 +1741,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mBubblesManagerOptional = bubblesManagerOptional;
|
mBubblesManagerOptional = bubblesManagerOptional;
|
||||||
mNotificationGutsManager = gutsManager;
|
mNotificationGutsManager = gutsManager;
|
||||||
mMetricsLogger = metricsLogger;
|
mMetricsLogger = metricsLogger;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDimens() {
|
private void initDimens() {
|
||||||
@@ -3588,11 +3633,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected void setPrivateLayout(NotificationContentView privateLayout) {
|
protected void setPrivateLayout(NotificationContentView privateLayout) {
|
||||||
mPrivateLayout = privateLayout;
|
mPrivateLayout = privateLayout;
|
||||||
|
mLayouts = new NotificationContentView[]{mPrivateLayout, mPublicLayout};
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected void setPublicLayout(NotificationContentView publicLayout) {
|
protected void setPublicLayout(NotificationContentView publicLayout) {
|
||||||
mPublicLayout = publicLayout;
|
mPublicLayout = publicLayout;
|
||||||
|
mLayouts = new NotificationContentView[]{mPrivateLayout, mPublicLayout};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -219,7 +219,8 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
mNotificationGutsManager,
|
mNotificationGutsManager,
|
||||||
mMetricsLogger,
|
mMetricsLogger,
|
||||||
mSmartReplyConstants,
|
mSmartReplyConstants,
|
||||||
mSmartReplyController
|
mSmartReplyController,
|
||||||
|
mFeatureFlags
|
||||||
);
|
);
|
||||||
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||||
if (mAllowLongPress) {
|
if (mAllowLongPress) {
|
||||||
|
|||||||
@@ -184,6 +184,8 @@ public class NotificationContentView extends FrameLayout implements Notification
|
|||||||
private boolean mRemoteInputVisible;
|
private boolean mRemoteInputVisible;
|
||||||
private int mUnrestrictedContentHeight;
|
private int mUnrestrictedContentHeight;
|
||||||
|
|
||||||
|
private boolean mContentAnimating;
|
||||||
|
|
||||||
public NotificationContentView(Context context, AttributeSet attrs) {
|
public NotificationContentView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
mHybridGroupManager = new HybridGroupManager(getContext());
|
mHybridGroupManager = new HybridGroupManager(getContext());
|
||||||
@@ -2129,8 +2131,49 @@ public class NotificationContentView extends FrameLayout implements Notification
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts and stops animations in the underlying views.
|
||||||
|
* Avoids restarting the animations by checking whether they're already running first.
|
||||||
|
* Return value is used for testing.
|
||||||
|
*
|
||||||
|
* @param running whether to start animations running, or stop them.
|
||||||
|
* @return true if the state of animations changed.
|
||||||
|
*/
|
||||||
|
public boolean setContentAnimationRunning(boolean running) {
|
||||||
|
boolean stateChangeRequired = (running != mContentAnimating);
|
||||||
|
if (stateChangeRequired) {
|
||||||
|
// Starts or stops the animations in the potential views.
|
||||||
|
if (mContractedWrapper != null) {
|
||||||
|
mContractedWrapper.setAnimationsRunning(running);
|
||||||
|
}
|
||||||
|
if (mExpandedWrapper != null) {
|
||||||
|
mExpandedWrapper.setAnimationsRunning(running);
|
||||||
|
}
|
||||||
|
if (mHeadsUpWrapper != null) {
|
||||||
|
mHeadsUpWrapper.setAnimationsRunning(running);
|
||||||
|
}
|
||||||
|
// Updates the state tracker.
|
||||||
|
mContentAnimating = running;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static class RemoteInputViewData {
|
private static class RemoteInputViewData {
|
||||||
@Nullable RemoteInputView mView;
|
@Nullable RemoteInputView mView;
|
||||||
@Nullable RemoteInputViewController mController;
|
@Nullable RemoteInputViewController mController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setContractedWrapper(NotificationViewWrapper contractedWrapper) {
|
||||||
|
mContractedWrapper = contractedWrapper;
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setExpandedWrapper(NotificationViewWrapper expandedWrapper) {
|
||||||
|
mExpandedWrapper = expandedWrapper;
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) {
|
||||||
|
mHeadsUpWrapper = headsUpWrapper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,15 @@ package com.android.systemui.statusbar.notification.row.wrapper;
|
|||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.widget.BigPictureNotificationImageView;
|
||||||
import com.android.systemui.statusbar.notification.ImageTransformState;
|
import com.android.systemui.statusbar.notification.ImageTransformState;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
|
|
||||||
@@ -31,6 +35,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
|||||||
*/
|
*/
|
||||||
public class NotificationBigPictureTemplateViewWrapper extends NotificationTemplateViewWrapper {
|
public class NotificationBigPictureTemplateViewWrapper extends NotificationTemplateViewWrapper {
|
||||||
|
|
||||||
|
private BigPictureNotificationImageView mImageView;
|
||||||
|
|
||||||
protected NotificationBigPictureTemplateViewWrapper(Context ctx, View view,
|
protected NotificationBigPictureTemplateViewWrapper(Context ctx, View view,
|
||||||
ExpandableNotificationRow row) {
|
ExpandableNotificationRow row) {
|
||||||
super(ctx, view, row);
|
super(ctx, view, row);
|
||||||
@@ -39,9 +45,14 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
|
|||||||
@Override
|
@Override
|
||||||
public void onContentUpdated(ExpandableNotificationRow row) {
|
public void onContentUpdated(ExpandableNotificationRow row) {
|
||||||
super.onContentUpdated(row);
|
super.onContentUpdated(row);
|
||||||
|
resolveViews();
|
||||||
updateImageTag(row.getEntry().getSbn());
|
updateImageTag(row.getEntry().getSbn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resolveViews() {
|
||||||
|
mImageView = mView.findViewById(R.id.big_picture);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateImageTag(StatusBarNotification sbn) {
|
private void updateImageTag(StatusBarNotification sbn) {
|
||||||
final Bundle extras = sbn.getNotification().extras;
|
final Bundle extras = sbn.getNotification().extras;
|
||||||
Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG, Icon.class);
|
Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG, Icon.class);
|
||||||
@@ -54,4 +65,25 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
|
|||||||
mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
|
mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or stops the animations in any drawables contained in this BigPicture Notification.
|
||||||
|
*
|
||||||
|
* @param running Whether the animations should be set to run.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAnimationsRunning(boolean running) {
|
||||||
|
if (mImageView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Drawable d = mImageView.getDrawable();
|
||||||
|
if (d instanceof AnimatedImageDrawable) {
|
||||||
|
AnimatedImageDrawable animatedImageDrawable = (AnimatedImageDrawable) d;
|
||||||
|
if (running) {
|
||||||
|
animatedImageDrawable.start();
|
||||||
|
} else {
|
||||||
|
animatedImageDrawable.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,20 @@
|
|||||||
package com.android.systemui.statusbar.notification.row.wrapper
|
package com.android.systemui.statusbar.notification.row.wrapper
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.android.internal.widget.CachingIconView
|
import com.android.internal.widget.CachingIconView
|
||||||
import com.android.internal.widget.ConversationLayout
|
import com.android.internal.widget.ConversationLayout
|
||||||
|
import com.android.internal.widget.MessagingGroup
|
||||||
|
import com.android.internal.widget.MessagingImageMessage
|
||||||
import com.android.internal.widget.MessagingLinearLayout
|
import com.android.internal.widget.MessagingLinearLayout
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.statusbar.notification.NotificationFadeAware
|
import com.android.systemui.statusbar.notification.NotificationFadeAware
|
||||||
import com.android.systemui.statusbar.notification.NotificationUtils
|
import com.android.systemui.statusbar.notification.NotificationUtils
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
||||||
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
|
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
|
||||||
|
import com.android.systemui.util.children
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a notification containing a conversation template
|
* Wraps a notification containing a conversation template
|
||||||
@@ -49,6 +53,7 @@ class NotificationConversationTemplateViewWrapper constructor(
|
|||||||
private lateinit var expandBtn: View
|
private lateinit var expandBtn: View
|
||||||
private lateinit var expandBtnContainer: View
|
private lateinit var expandBtnContainer: View
|
||||||
private lateinit var imageMessageContainer: ViewGroup
|
private lateinit var imageMessageContainer: ViewGroup
|
||||||
|
private lateinit var messageContainers: ArrayList<MessagingGroup>
|
||||||
private lateinit var messagingLinearLayout: MessagingLinearLayout
|
private lateinit var messagingLinearLayout: MessagingLinearLayout
|
||||||
private lateinit var conversationTitleView: View
|
private lateinit var conversationTitleView: View
|
||||||
private lateinit var importanceRing: View
|
private lateinit var importanceRing: View
|
||||||
@@ -60,6 +65,7 @@ class NotificationConversationTemplateViewWrapper constructor(
|
|||||||
private fun resolveViews() {
|
private fun resolveViews() {
|
||||||
messagingLinearLayout = conversationLayout.messagingLinearLayout
|
messagingLinearLayout = conversationLayout.messagingLinearLayout
|
||||||
imageMessageContainer = conversationLayout.imageMessageContainer
|
imageMessageContainer = conversationLayout.imageMessageContainer
|
||||||
|
messageContainers = conversationLayout.messagingGroups
|
||||||
with(conversationLayout) {
|
with(conversationLayout) {
|
||||||
conversationIconContainer =
|
conversationIconContainer =
|
||||||
requireViewById(com.android.internal.R.id.conversation_icon_container)
|
requireViewById(com.android.internal.R.id.conversation_icon_container)
|
||||||
@@ -146,4 +152,26 @@ class NotificationConversationTemplateViewWrapper constructor(
|
|||||||
NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
|
NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
|
||||||
NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
|
NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Starts or stops the animations in any drawables contained in this Conversation Notification.
|
||||||
|
override fun setAnimationsRunning(running: Boolean) {
|
||||||
|
// We apply to both the child message containers in a conversation group,
|
||||||
|
// and the top level image message container.
|
||||||
|
val containers = messageContainers.asSequence().map { it.messageContainer } +
|
||||||
|
sequenceOf(imageMessageContainer)
|
||||||
|
val drawables =
|
||||||
|
containers
|
||||||
|
.flatMap { it.children }
|
||||||
|
.mapNotNull { child ->
|
||||||
|
(child as? MessagingImageMessage)?.let { imageMessage ->
|
||||||
|
imageMessage.drawable as? AnimatedImageDrawable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawables.toSet().forEach {
|
||||||
|
when {
|
||||||
|
running -> it.start()
|
||||||
|
!running -> it.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,13 @@
|
|||||||
package com.android.systemui.statusbar.notification.row.wrapper;
|
package com.android.systemui.statusbar.notification.row.wrapper;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.android.internal.widget.MessagingGroup;
|
||||||
|
import com.android.internal.widget.MessagingImageMessage;
|
||||||
import com.android.internal.widget.MessagingLayout;
|
import com.android.internal.widget.MessagingLayout;
|
||||||
import com.android.internal.widget.MessagingLinearLayout;
|
import com.android.internal.widget.MessagingLinearLayout;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
@@ -127,4 +131,40 @@ public class NotificationMessagingTemplateViewWrapper extends NotificationTempla
|
|||||||
}
|
}
|
||||||
return super.getMinLayoutHeight();
|
return super.getMinLayoutHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or stops the animations in any drawables contained in this Messaging Notification.
|
||||||
|
*
|
||||||
|
* @param running Whether the animations should be set to run.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAnimationsRunning(boolean running) {
|
||||||
|
if (mMessagingLayout == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MessagingGroup group : mMessagingLayout.getMessagingGroups()) {
|
||||||
|
for (int i = 0; i < group.getMessageContainer().getChildCount(); i++) {
|
||||||
|
View view = group.getMessageContainer().getChildAt(i);
|
||||||
|
// We only need to set animations in MessagingImageMessages.
|
||||||
|
if (!(view instanceof MessagingImageMessage)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MessagingImageMessage imageMessage =
|
||||||
|
(com.android.internal.widget.MessagingImageMessage) view;
|
||||||
|
|
||||||
|
// If the drawable isn't an AnimatedImageDrawable, we can't set it to animate.
|
||||||
|
Drawable d = imageMessage.getDrawable();
|
||||||
|
if (!(d instanceof AnimatedImageDrawable)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AnimatedImageDrawable animatedImageDrawable = (AnimatedImageDrawable) d;
|
||||||
|
if (running) {
|
||||||
|
animatedImageDrawable.start();
|
||||||
|
} else {
|
||||||
|
animatedImageDrawable.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,4 +403,12 @@ public abstract class NotificationViewWrapper implements TransformableView {
|
|||||||
NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
|
NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
|
||||||
NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
|
NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or stops the animations in any drawables contained in this Notification.
|
||||||
|
*
|
||||||
|
* @param running Whether the animations should be set to run.
|
||||||
|
*/
|
||||||
|
public void setAnimationsRunning(boolean running) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3132,7 +3132,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
private void updateAnimationState(boolean running, View child) {
|
private void updateAnimationState(boolean running, View child) {
|
||||||
if (child instanceof ExpandableNotificationRow) {
|
if (child instanceof ExpandableNotificationRow) {
|
||||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||||
row.setIconAnimationRunning(running);
|
row.setAnimationRunning(running);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,16 +44,23 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
|
import android.graphics.drawable.AnimationDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
import android.testing.TestableLooper.RunWithLooper;
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.widget.CachingIconView;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
||||||
@@ -61,6 +68,7 @@ import com.android.systemui.statusbar.notification.FeedbackIcon;
|
|||||||
import com.android.systemui.statusbar.notification.SourceType;
|
import com.android.systemui.statusbar.notification.SourceType;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
|
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
|
||||||
|
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
|
||||||
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
|
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
@@ -72,6 +80,7 @@ import org.mockito.Mockito;
|
|||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -96,6 +105,9 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
mDependency,
|
mDependency,
|
||||||
TestableLooper.get(this));
|
TestableLooper.get(this));
|
||||||
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
|
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
|
||||||
|
FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
|
||||||
|
fakeFeatureFlags.set(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE, true);
|
||||||
|
mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
|
||||||
// create a standard private notification row
|
// create a standard private notification row
|
||||||
Notification normalNotif = mNotificationTestHelper.createNotification();
|
Notification normalNotif = mNotificationTestHelper.createNotification();
|
||||||
normalNotif.publicVersion = null;
|
normalNotif.publicVersion = null;
|
||||||
@@ -559,4 +571,123 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
Assert.assertEquals(1f, mGroupRow.getBottomRoundness(), 0.001f);
|
Assert.assertEquals(1f, mGroupRow.getBottomRoundness(), 0.001f);
|
||||||
Assert.assertEquals(1f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
|
Assert.assertEquals(1f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetContentAnimationRunning_Run() throws Exception {
|
||||||
|
// Create views for the notification row.
|
||||||
|
NotificationContentView publicLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPublicLayout(publicLayout);
|
||||||
|
NotificationContentView privateLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPrivateLayout(privateLayout);
|
||||||
|
|
||||||
|
mNotifRow.setAnimationRunning(true);
|
||||||
|
verify(publicLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
verify(privateLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetContentAnimationRunning_Stop() {
|
||||||
|
// Create views for the notification row.
|
||||||
|
NotificationContentView publicLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPublicLayout(publicLayout);
|
||||||
|
NotificationContentView privateLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPrivateLayout(privateLayout);
|
||||||
|
|
||||||
|
mNotifRow.setAnimationRunning(false);
|
||||||
|
verify(publicLayout, times(1)).setContentAnimationRunning(false);
|
||||||
|
verify(privateLayout, times(1)).setContentAnimationRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetContentAnimationRunningInGroupChild_Run() {
|
||||||
|
// Creates parent views on mGroupRow.
|
||||||
|
NotificationContentView publicParentLayout = mock(NotificationContentView.class);
|
||||||
|
mGroupRow.setPublicLayout(publicParentLayout);
|
||||||
|
NotificationContentView privateParentLayout = mock(NotificationContentView.class);
|
||||||
|
mGroupRow.setPrivateLayout(privateParentLayout);
|
||||||
|
|
||||||
|
// Create child views on mNotifRow.
|
||||||
|
NotificationContentView publicChildLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPublicLayout(publicChildLayout);
|
||||||
|
NotificationContentView privateChildLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPrivateLayout(privateChildLayout);
|
||||||
|
when(mNotifRow.isGroupExpanded()).thenReturn(true);
|
||||||
|
setMockChildrenContainer(mGroupRow, mNotifRow);
|
||||||
|
|
||||||
|
mGroupRow.setAnimationRunning(true);
|
||||||
|
verify(publicParentLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
verify(privateParentLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
// The child layouts should be started too.
|
||||||
|
verify(publicChildLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
verify(privateChildLayout, times(1)).setContentAnimationRunning(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetIconAnimationRunningGroup_Run() {
|
||||||
|
// Create views for a group row.
|
||||||
|
NotificationContentView publicParentLayout = mock(NotificationContentView.class);
|
||||||
|
mGroupRow.setPublicLayout(publicParentLayout);
|
||||||
|
NotificationContentView privateParentLayout = mock(NotificationContentView.class);
|
||||||
|
mGroupRow.setPrivateLayout(privateParentLayout);
|
||||||
|
when(mGroupRow.isGroupExpanded()).thenReturn(true);
|
||||||
|
|
||||||
|
// Sets up mNotifRow as a child ExpandableNotificationRow.
|
||||||
|
NotificationContentView publicChildLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPublicLayout(publicChildLayout);
|
||||||
|
NotificationContentView privateChildLayout = mock(NotificationContentView.class);
|
||||||
|
mNotifRow.setPrivateLayout(privateChildLayout);
|
||||||
|
when(mNotifRow.isGroupExpanded()).thenReturn(true);
|
||||||
|
|
||||||
|
NotificationChildrenContainer mockContainer =
|
||||||
|
setMockChildrenContainer(mGroupRow, mNotifRow);
|
||||||
|
|
||||||
|
// Mock the children view wrappers, and give them each an icon.
|
||||||
|
NotificationViewWrapper mockViewWrapper = mock(NotificationViewWrapper.class);
|
||||||
|
when(mockContainer.getNotificationViewWrapper()).thenReturn(mockViewWrapper);
|
||||||
|
CachingIconView mockIcon = mock(CachingIconView.class);
|
||||||
|
when(mockViewWrapper.getIcon()).thenReturn(mockIcon);
|
||||||
|
|
||||||
|
NotificationViewWrapper mockLowPriorityViewWrapper = mock(NotificationViewWrapper.class);
|
||||||
|
when(mockContainer.getLowPriorityViewWrapper()).thenReturn(mockLowPriorityViewWrapper);
|
||||||
|
CachingIconView mockLowPriorityIcon = mock(CachingIconView.class);
|
||||||
|
when(mockLowPriorityViewWrapper.getIcon()).thenReturn(mockLowPriorityIcon);
|
||||||
|
|
||||||
|
// Give the icon image views drawables, so we can make sure they animate.
|
||||||
|
// We use both AnimationDrawables and AnimatedVectorDrawables to ensure both work.
|
||||||
|
AnimationDrawable drawable = mock(AnimationDrawable.class);
|
||||||
|
AnimatedVectorDrawable vectorDrawable = mock(AnimatedVectorDrawable.class);
|
||||||
|
setDrawableIconsInImageView(mockIcon, drawable, vectorDrawable);
|
||||||
|
|
||||||
|
AnimationDrawable lowPriDrawable = mock(AnimationDrawable.class);
|
||||||
|
AnimatedVectorDrawable lowPriVectorDrawable = mock(AnimatedVectorDrawable.class);
|
||||||
|
setDrawableIconsInImageView(mockLowPriorityIcon, lowPriDrawable, lowPriVectorDrawable);
|
||||||
|
|
||||||
|
mGroupRow.setAnimationRunning(true);
|
||||||
|
verify(drawable, times(1)).start();
|
||||||
|
verify(vectorDrawable, times(1)).start();
|
||||||
|
verify(lowPriDrawable, times(1)).start();
|
||||||
|
verify(lowPriVectorDrawable, times(1)).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable,
|
||||||
|
Drawable rightIconDrawable) {
|
||||||
|
ImageView iconView = mock(ImageView.class);
|
||||||
|
when(icon.findViewById(com.android.internal.R.id.icon)).thenReturn(iconView);
|
||||||
|
when(iconView.getDrawable()).thenReturn(iconDrawable);
|
||||||
|
|
||||||
|
ImageView rightIconView = mock(ImageView.class);
|
||||||
|
when(icon.findViewById(com.android.internal.R.id.right_icon)).thenReturn(rightIconView);
|
||||||
|
when(rightIconView.getDrawable()).thenReturn(rightIconDrawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationChildrenContainer setMockChildrenContainer(
|
||||||
|
ExpandableNotificationRow parentRow, ExpandableNotificationRow childRow) {
|
||||||
|
List<ExpandableNotificationRow> rowList = Arrays.asList(childRow);
|
||||||
|
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
||||||
|
when(mockContainer.getNotificationChildCount()).thenReturn(1);
|
||||||
|
when(mockContainer.getAttachedChildren()).thenReturn(rowList);
|
||||||
|
parentRow.setChildrenContainer(mockContainer);
|
||||||
|
return mockContainer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,12 @@ import com.android.systemui.media.dialog.MediaOutputDialogFactory
|
|||||||
import com.android.systemui.statusbar.notification.FeedbackIcon
|
import com.android.systemui.statusbar.notification.FeedbackIcon
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
|
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
|
||||||
|
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import junit.framework.Assert.assertEquals
|
import junit.framework.Assert.assertEquals
|
||||||
|
import junit.framework.Assert.assertFalse
|
||||||
|
import junit.framework.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -44,6 +47,7 @@ import org.mockito.Mock
|
|||||||
import org.mockito.Mockito.doReturn
|
import org.mockito.Mockito.doReturn
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.spy
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.Mockito.times
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations.initMocks
|
import org.mockito.MockitoAnnotations.initMocks
|
||||||
|
|
||||||
@@ -305,6 +309,86 @@ class NotificationContentViewTest : SysuiTestCase() {
|
|||||||
assertEquals(0, getMarginBottom(actionListMarginTarget))
|
assertEquals(0, getMarginBottom(actionListMarginTarget))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSetAnimationRunning() {
|
||||||
|
// Given: contractedWrapper, enpandedWrapper, and headsUpWrapper being set
|
||||||
|
val mockContracted = mock<NotificationViewWrapper>()
|
||||||
|
val mockExpanded = mock<NotificationViewWrapper>()
|
||||||
|
val mockHeadsUp = mock<NotificationViewWrapper>()
|
||||||
|
|
||||||
|
view.setContractedWrapper(mockContracted)
|
||||||
|
view.setExpandedWrapper(mockExpanded)
|
||||||
|
view.setHeadsUpWrapper(mockHeadsUp)
|
||||||
|
|
||||||
|
// When: we set content animation running.
|
||||||
|
assertTrue(view.setContentAnimationRunning(true))
|
||||||
|
|
||||||
|
// Then: contractedChild, expandedChild, and headsUpChild should have setAnimationsRunning
|
||||||
|
// called on them.
|
||||||
|
verify(mockContracted, times(1)).setAnimationsRunning(true)
|
||||||
|
verify(mockExpanded, times(1)).setAnimationsRunning(true)
|
||||||
|
verify(mockHeadsUp, times(1)).setAnimationsRunning(true)
|
||||||
|
|
||||||
|
// When: we set content animation running true _again_.
|
||||||
|
assertFalse(view.setContentAnimationRunning(true))
|
||||||
|
|
||||||
|
// Then: the children should not have setAnimationRunning called on them again.
|
||||||
|
// Verify counts number of calls so far on the object, so these still register as 1.
|
||||||
|
verify(mockContracted, times(1)).setAnimationsRunning(true)
|
||||||
|
verify(mockExpanded, times(1)).setAnimationsRunning(true)
|
||||||
|
verify(mockHeadsUp, times(1)).setAnimationsRunning(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSetAnimationStopped() {
|
||||||
|
// Given: contractedWrapper, expandedWrapper, and headsUpWrapper being set
|
||||||
|
val mockContracted = mock<NotificationViewWrapper>()
|
||||||
|
val mockExpanded = mock<NotificationViewWrapper>()
|
||||||
|
val mockHeadsUp = mock<NotificationViewWrapper>()
|
||||||
|
|
||||||
|
view.setContractedWrapper(mockContracted)
|
||||||
|
view.setExpandedWrapper(mockExpanded)
|
||||||
|
view.setHeadsUpWrapper(mockHeadsUp)
|
||||||
|
|
||||||
|
// When: we set content animation running.
|
||||||
|
assertTrue(view.setContentAnimationRunning(true))
|
||||||
|
|
||||||
|
// Then: contractedChild, expandedChild, and headsUpChild should have setAnimationsRunning
|
||||||
|
// called on them.
|
||||||
|
verify(mockContracted).setAnimationsRunning(true)
|
||||||
|
verify(mockExpanded).setAnimationsRunning(true)
|
||||||
|
verify(mockHeadsUp).setAnimationsRunning(true)
|
||||||
|
|
||||||
|
// When: we set content animation running false, the state changes, so the function
|
||||||
|
// returns true.
|
||||||
|
assertTrue(view.setContentAnimationRunning(false))
|
||||||
|
|
||||||
|
// Then: the children have their animations stopped.
|
||||||
|
verify(mockContracted).setAnimationsRunning(false)
|
||||||
|
verify(mockExpanded).setAnimationsRunning(false)
|
||||||
|
verify(mockHeadsUp).setAnimationsRunning(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSetAnimationInitStopped() {
|
||||||
|
// Given: contractedWrapper, expandedWrapper, and headsUpWrapper being set
|
||||||
|
val mockContracted = mock<NotificationViewWrapper>()
|
||||||
|
val mockExpanded = mock<NotificationViewWrapper>()
|
||||||
|
val mockHeadsUp = mock<NotificationViewWrapper>()
|
||||||
|
|
||||||
|
view.setContractedWrapper(mockContracted)
|
||||||
|
view.setExpandedWrapper(mockExpanded)
|
||||||
|
view.setHeadsUpWrapper(mockHeadsUp)
|
||||||
|
|
||||||
|
// When: we try to stop the animations before they've been started.
|
||||||
|
assertFalse(view.setContentAnimationRunning(false))
|
||||||
|
|
||||||
|
// Then: the children should not have setAnimationRunning called on them again.
|
||||||
|
verify(mockContracted, never()).setAnimationsRunning(false)
|
||||||
|
verify(mockExpanded, never()).setAnimationsRunning(false)
|
||||||
|
verify(mockHeadsUp, never()).setAnimationsRunning(false)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createMockContainingNotification(notificationEntry: NotificationEntry) =
|
private fun createMockContainingNotification(notificationEntry: NotificationEntry) =
|
||||||
mock<ExpandableNotificationRow>().apply {
|
mock<ExpandableNotificationRow>().apply {
|
||||||
whenever(this.entry).thenReturn(notificationEntry)
|
whenever(this.entry).thenReturn(notificationEntry)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.systemui.TestableDependency;
|
import com.android.systemui.TestableDependency;
|
||||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||||
import com.android.systemui.classifier.FalsingManagerFake;
|
import com.android.systemui.classifier.FalsingManagerFake;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.media.controls.util.MediaFeatureFlag;
|
import com.android.systemui.media.controls.util.MediaFeatureFlag;
|
||||||
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
|
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
@@ -130,6 +131,7 @@ public class NotificationTestHelper {
|
|||||||
public final OnUserInteractionCallback mOnUserInteractionCallback;
|
public final OnUserInteractionCallback mOnUserInteractionCallback;
|
||||||
public final Runnable mFutureDismissalRunnable;
|
public final Runnable mFutureDismissalRunnable;
|
||||||
private @InflationFlag int mDefaultInflationFlags;
|
private @InflationFlag int mDefaultInflationFlags;
|
||||||
|
private FeatureFlags mFeatureFlags;
|
||||||
|
|
||||||
public NotificationTestHelper(
|
public NotificationTestHelper(
|
||||||
Context context,
|
Context context,
|
||||||
@@ -193,12 +195,17 @@ public class NotificationTestHelper {
|
|||||||
mFutureDismissalRunnable = mock(Runnable.class);
|
mFutureDismissalRunnable = mock(Runnable.class);
|
||||||
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
|
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
|
||||||
.thenReturn(mFutureDismissalRunnable);
|
.thenReturn(mFutureDismissalRunnable);
|
||||||
|
mFeatureFlags = mock(FeatureFlags.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
|
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
|
||||||
mDefaultInflationFlags = defaultInflationFlags;
|
mDefaultInflationFlags = defaultInflationFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFeatureFlags(FeatureFlags featureFlags) {
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
|
}
|
||||||
|
|
||||||
public ExpandableNotificationRowLogger getMockLogger() {
|
public ExpandableNotificationRowLogger getMockLogger() {
|
||||||
return mMockLogger;
|
return mMockLogger;
|
||||||
}
|
}
|
||||||
@@ -561,7 +568,8 @@ public class NotificationTestHelper {
|
|||||||
mock(NotificationGutsManager.class),
|
mock(NotificationGutsManager.class),
|
||||||
mock(MetricsLogger.class),
|
mock(MetricsLogger.class),
|
||||||
mock(SmartReplyConstants.class),
|
mock(SmartReplyConstants.class),
|
||||||
mock(SmartReplyController.class));
|
mock(SmartReplyController.class),
|
||||||
|
mFeatureFlags);
|
||||||
|
|
||||||
row.setAboveShelfChangedListener(aboveShelf -> { });
|
row.setAboveShelfChangedListener(aboveShelf -> { });
|
||||||
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
|
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
|
||||||
|
|||||||
@@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.notification.row.wrapper;
|
package com.android.systemui.statusbar.notification.row.wrapper;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.graphics.drawable.AnimatedImageDrawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
@@ -28,11 +29,11 @@ import android.testing.TestableLooper;
|
|||||||
import android.testing.TestableLooper.RunWithLooper;
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.widget.BigPictureNotificationImageView;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
|
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
|
||||||
@@ -73,4 +74,38 @@ public class NotificationBigPictureTemplateViewWrapperTest extends SysuiTestCase
|
|||||||
Notification.EXTRA_LARGE_ICON_BIG, new Bundle());
|
Notification.EXTRA_LARGE_ICON_BIG, new Bundle());
|
||||||
wrapper.onContentUpdated(mRow);
|
wrapper.onContentUpdated(mRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAnimationsRunning_Run() {
|
||||||
|
BigPictureNotificationImageView imageView = mView.findViewById(R.id.big_picture);
|
||||||
|
AnimatedImageDrawable mockDrawable = mock(AnimatedImageDrawable.class);
|
||||||
|
|
||||||
|
assertNotNull(imageView);
|
||||||
|
imageView.setImageDrawable(mockDrawable);
|
||||||
|
|
||||||
|
NotificationViewWrapper wrapper = new NotificationBigPictureTemplateViewWrapper(mContext,
|
||||||
|
mView, mRow);
|
||||||
|
// Required to re-initialize the imageView to the imageView created above.
|
||||||
|
wrapper.onContentUpdated(mRow);
|
||||||
|
|
||||||
|
wrapper.setAnimationsRunning(true);
|
||||||
|
verify(mockDrawable).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAnimationsRunning_Stop() {
|
||||||
|
BigPictureNotificationImageView imageView = mView.findViewById(R.id.big_picture);
|
||||||
|
AnimatedImageDrawable mockDrawable = mock(AnimatedImageDrawable.class);
|
||||||
|
|
||||||
|
assertNotNull(imageView);
|
||||||
|
imageView.setImageDrawable(mockDrawable);
|
||||||
|
|
||||||
|
NotificationViewWrapper wrapper = new NotificationBigPictureTemplateViewWrapper(mContext,
|
||||||
|
mView, mRow);
|
||||||
|
// Required to re-initialize the imageView to the imageView created above.
|
||||||
|
wrapper.onContentUpdated(mRow);
|
||||||
|
|
||||||
|
wrapper.setAnimationsRunning(false);
|
||||||
|
verify(mockDrawable).stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.statusbar.notification.row.wrapper
|
||||||
|
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import android.testing.TestableLooper.RunWithLooper
|
||||||
|
import android.view.View
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.R
|
||||||
|
import com.android.internal.widget.CachingIconView
|
||||||
|
import com.android.internal.widget.ConversationLayout
|
||||||
|
import com.android.internal.widget.MessagingGroup
|
||||||
|
import com.android.internal.widget.MessagingImageMessage
|
||||||
|
import com.android.internal.widget.MessagingLinearLayout
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
||||||
|
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@RunWithLooper
|
||||||
|
class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var mRow: ExpandableNotificationRow
|
||||||
|
private lateinit var helper: NotificationTestHelper
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
allowTestableLooperAsMainThread()
|
||||||
|
helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
|
||||||
|
mRow = helper.createRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setAnimationsRunning_Run() {
|
||||||
|
// Creates a mocked out NotificationEntry of ConversationLayout type,
|
||||||
|
// with a mock imageMessage.drawable embedded in its MessagingImageMessages
|
||||||
|
// (both top level, and in a group).
|
||||||
|
val mockDrawable = mock<AnimatedImageDrawable>()
|
||||||
|
val mockDrawable2 = mock<AnimatedImageDrawable>()
|
||||||
|
val mockLayoutView: View = fakeConversationLayout(mockDrawable, mockDrawable2)
|
||||||
|
|
||||||
|
val wrapper: NotificationViewWrapper =
|
||||||
|
NotificationConversationTemplateViewWrapper(mContext, mockLayoutView, mRow)
|
||||||
|
wrapper.onContentUpdated(mRow)
|
||||||
|
wrapper.setAnimationsRunning(true)
|
||||||
|
|
||||||
|
// Verifies that each AnimatedImageDrawable is started animating.
|
||||||
|
verify(mockDrawable).start()
|
||||||
|
verify(mockDrawable2).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setAnimationsRunning_Stop() {
|
||||||
|
// Creates a mocked out NotificationEntry of ConversationLayout type,
|
||||||
|
// with a mock imageMessage.drawable embedded in its MessagingImageMessages
|
||||||
|
// (both top level, and in a group).
|
||||||
|
val mockDrawable = mock<AnimatedImageDrawable>()
|
||||||
|
val mockDrawable2 = mock<AnimatedImageDrawable>()
|
||||||
|
val mockLayoutView: View = fakeConversationLayout(mockDrawable, mockDrawable2)
|
||||||
|
|
||||||
|
val wrapper: NotificationViewWrapper =
|
||||||
|
NotificationConversationTemplateViewWrapper(mContext, mockLayoutView, mRow)
|
||||||
|
wrapper.onContentUpdated(mRow)
|
||||||
|
wrapper.setAnimationsRunning(false)
|
||||||
|
|
||||||
|
// Verifies that each AnimatedImageDrawable is started animating.
|
||||||
|
verify(mockDrawable).stop()
|
||||||
|
verify(mockDrawable2).stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fakeConversationLayout(
|
||||||
|
mockDrawableGroupMessage: AnimatedImageDrawable,
|
||||||
|
mockDrawableImageMessage: AnimatedImageDrawable
|
||||||
|
): View {
|
||||||
|
val mockMessagingImageMessage: MessagingImageMessage =
|
||||||
|
mock<MessagingImageMessage>().apply {
|
||||||
|
whenever(drawable).thenReturn(mockDrawableImageMessage)
|
||||||
|
}
|
||||||
|
val mockImageMessageContainer: MessagingLinearLayout =
|
||||||
|
mock<MessagingLinearLayout>().apply {
|
||||||
|
whenever(childCount).thenReturn(1)
|
||||||
|
whenever(getChildAt(any())).thenReturn(mockMessagingImageMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mockMessagingImageMessageForGroup: MessagingImageMessage =
|
||||||
|
mock<MessagingImageMessage>().apply {
|
||||||
|
whenever(drawable).thenReturn(mockDrawableGroupMessage)
|
||||||
|
}
|
||||||
|
val mockMessageContainer: MessagingLinearLayout =
|
||||||
|
mock<MessagingLinearLayout>().apply {
|
||||||
|
whenever(childCount).thenReturn(1)
|
||||||
|
whenever(getChildAt(any())).thenReturn(mockMessagingImageMessageForGroup)
|
||||||
|
}
|
||||||
|
val mockGroup: MessagingGroup =
|
||||||
|
mock<MessagingGroup>().apply {
|
||||||
|
whenever(messageContainer).thenReturn(mockMessageContainer)
|
||||||
|
}
|
||||||
|
val mockView: View =
|
||||||
|
mock<ConversationLayout>().apply {
|
||||||
|
whenever(messagingGroups).thenReturn(ArrayList<MessagingGroup>(listOf(mockGroup)))
|
||||||
|
whenever(imageMessageContainer).thenReturn(mockImageMessageContainer)
|
||||||
|
whenever(messagingLinearLayout).thenReturn(mockMessageContainer)
|
||||||
|
|
||||||
|
// These must be mocked as they're required to be nonnull.
|
||||||
|
whenever(requireViewById<View>(R.id.conversation_icon_container)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<CachingIconView>(R.id.conversation_icon))
|
||||||
|
.thenReturn(mock())
|
||||||
|
whenever(findViewById<CachingIconView>(R.id.icon)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.conversation_icon_badge_bg)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.expand_button)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.expand_button_container)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.conversation_icon_badge_ring))
|
||||||
|
.thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.app_name_text)).thenReturn(mock())
|
||||||
|
whenever(requireViewById<View>(R.id.conversation_text)).thenReturn(mock())
|
||||||
|
}
|
||||||
|
return mockView
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.statusbar.notification.row.wrapper
|
||||||
|
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import android.testing.TestableLooper.RunWithLooper
|
||||||
|
import android.view.View
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.widget.MessagingGroup
|
||||||
|
import com.android.internal.widget.MessagingImageMessage
|
||||||
|
import com.android.internal.widget.MessagingLayout
|
||||||
|
import com.android.internal.widget.MessagingLinearLayout
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
||||||
|
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@RunWithLooper
|
||||||
|
class NotificationMessagingTemplateViewWrapperTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var mRow: ExpandableNotificationRow
|
||||||
|
private lateinit var helper: NotificationTestHelper
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
allowTestableLooperAsMainThread()
|
||||||
|
helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
|
||||||
|
mRow = helper.createRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setAnimationsRunning_Run() {
|
||||||
|
// Creates a mocked out NotificationEntry of MessagingLayout/MessagingStyle type,
|
||||||
|
// with a mock imageMessage.drawable embedded in its MessagingImageMessage.
|
||||||
|
val mockDrawable = mock<AnimatedImageDrawable>()
|
||||||
|
val mockLayoutView: View = fakeMessagingLayout(mockDrawable)
|
||||||
|
|
||||||
|
val wrapper: NotificationViewWrapper =
|
||||||
|
NotificationMessagingTemplateViewWrapper(mContext, mockLayoutView, mRow)
|
||||||
|
wrapper.setAnimationsRunning(true)
|
||||||
|
|
||||||
|
// Verifies that each AnimatedImageDrawable is started animating.
|
||||||
|
verify(mockDrawable).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setAnimationsRunning_Stop() {
|
||||||
|
// Creates a mocked out NotificationEntry of MessagingLayout/MessagingStyle type,
|
||||||
|
// with a mock imageMessage.drawable embedded in its MessagingImageMessage.
|
||||||
|
val mockDrawable = mock<AnimatedImageDrawable>()
|
||||||
|
val mockLayoutView: View = fakeMessagingLayout(mockDrawable)
|
||||||
|
|
||||||
|
val wrapper: NotificationViewWrapper =
|
||||||
|
NotificationMessagingTemplateViewWrapper(mContext, mockLayoutView, mRow)
|
||||||
|
wrapper.setAnimationsRunning(false)
|
||||||
|
|
||||||
|
// Verifies that each AnimatedImageDrawable is started animating.
|
||||||
|
verify(mockDrawable).stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fakeMessagingLayout(mockDrawable: AnimatedImageDrawable): View {
|
||||||
|
val mockMessagingImageMessage: MessagingImageMessage =
|
||||||
|
mock<MessagingImageMessage>().apply { whenever(drawable).thenReturn(mockDrawable) }
|
||||||
|
val mockMessageContainer: MessagingLinearLayout =
|
||||||
|
mock<MessagingLinearLayout>().apply {
|
||||||
|
whenever(childCount).thenReturn(1)
|
||||||
|
whenever(getChildAt(any())).thenReturn(mockMessagingImageMessage)
|
||||||
|
}
|
||||||
|
val mockGroup: MessagingGroup =
|
||||||
|
mock<MessagingGroup>().apply {
|
||||||
|
whenever(messageContainer).thenReturn(mockMessageContainer)
|
||||||
|
}
|
||||||
|
val mockView: View =
|
||||||
|
mock<MessagingLayout>().apply {
|
||||||
|
whenever(messagingGroups).thenReturn(ArrayList<MessagingGroup>(listOf(mockGroup)))
|
||||||
|
}
|
||||||
|
return mockView
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user