Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 370e1f90 authored by Shery Sheng's avatar Shery Sheng
Browse files

[Notification] Show animated replies/actions before regular ones

Adjust the ordering of smart actions and replies in notifications so that animated
replies come first, followed by animated actions, then regular smart replies and
smart actions. Also adjust the prioritization for which buttons will be hidden when
they can't all fit in the notification accordingly.

Summary (Test executed with 1 devices.)
-------
arm64-v8a SystemUITests: Passed: 53, Failed: 0, Ignored: 0, Assumption Failed: 0

All tests passed!

Test logs: /tmp/atest_result_sherysheng/20250521_205247_nyqhggx5/log
Log file list: file:///tmp/atest_result_sherysheng/20250521_205247_nyqhggx5/local_log_file_list.html

Bug: 417356447
Test: local build and test pass
Flag: EXEMPT new feature request to change the smart replies and actions
chips order

Change-Id: I713b8baf512aee98d62a1dff21c577e9e72f073c
parent 02656a28
Loading
Loading
Loading
Loading
+66 −22
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.service.notification.StatusBarNotification
import android.text.Annotation
import android.text.SpannableStringBuilder
import android.text.Spanned
@@ -46,7 +47,6 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.Button
import android.service.notification.StatusBarNotification
import androidx.appcompat.content.res.AppCompatResources
import com.android.systemui.Flags
import com.android.systemui.plugins.ActivityStarter
@@ -138,6 +138,42 @@ constructor(
    override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState =
        chooseSmartRepliesAndActions(entry)

    private fun Button.getButtonType(): SmartButtonType? {
        return (this.layoutParams as? SmartReplyView.LayoutParams)?.mButtonType
    }

    private fun getSortedButtons(
        smartReplyButtons: List<Button>,
        smartActionButtons: List<Button>
    ): List<Button> {
        val finalCombinedButtons: List<Button>

        if (Flags.notificationAnimatedActionsTreatment()) {
            // Order is Animated Replies -> Animated Actions -> Smart Replies -> Smart Actions

            // Filter buttons based on their type for the animated treatment
            val animatedReplyButtons = smartReplyButtons.filter {
                it.getButtonType() == SmartButtonType.ANIMATED_REPLY
            }
            val regularReplyButtons = smartReplyButtons.filter {
                it.getButtonType() == SmartButtonType.REPLY
            }

            val animatedActionButtons = smartActionButtons.filter {
                it.getButtonType() == SmartButtonType.ANIMATED_ACTION
            }
            val regularActionButtons = smartActionButtons.filter {
                it.getButtonType() == SmartButtonType.ACTION
            }

            finalCombinedButtons =
                animatedReplyButtons + animatedActionButtons + regularReplyButtons + regularActionButtons
        } else {
            // Order is Smart Replies -> Smart Actions
            finalCombinedButtons = smartReplyButtons + smartActionButtons
        }
        return finalCombinedButtons
    }
    override fun inflateSmartReplyViewHolder(
        sysuiContext: Context,
        notifPackageContext: Context,
@@ -147,7 +183,7 @@ constructor(
    ): InflatedSmartReplyViewHolder {
        if (!shouldShowSmartReplyView(entry.sbn, newSmartReplyState)) {
            return InflatedSmartReplyViewHolder(
                null /* smartReplyView */,
                null, /* smartReplyView */
                null, /* smartSuggestionButtons */
            )
        }
@@ -174,7 +210,7 @@ constructor(
                        delayOnClickListener,
                    )
                }
            } ?: emptySequence()
            } ?.toList() ?: emptyList()

        val smartActionButtons =
            newSmartReplyState.smartActions?.let { smartActions ->
@@ -194,14 +230,13 @@ constructor(
                            themedPackageContext,
                        )
                    }
            } ?: emptySequence()
            }?.toList() ?: emptyList()

        return InflatedSmartReplyViewHolder(
            smartReplyView,
            (smartReplyButtons + smartActionButtons).toList(),
            getSortedButtons(smartReplyButtons, smartActionButtons)
        )
    }

    /**
     * Chose what smart replies and smart actions to display. App generated suggestions take
     * precedence. So if the app provides any smart replies, we don't show any replies or actions
@@ -448,9 +483,14 @@ constructor(
                        DelayedOnClickListener(onClickListener, constants.onClickInitDelay)
                    else onClickListener
                )

                if (isAnimatedAction) {
                    (layoutParams as SmartReplyView.LayoutParams).mButtonType =
                        SmartButtonType.ANIMATED_ACTION
                } else {
                    // Mark this as an Action button
                (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.ACTION
                    (layoutParams as SmartReplyView.LayoutParams).mButtonType =
                        SmartButtonType.ACTION
                }
            }
    }

@@ -579,9 +619,13 @@ constructor(
                            info.addAction(action)
                        }
                    }
                // TODO: probably shouldn't do this here, bad API
                // Mark this as a Reply button
                (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.REPLY
                if (enableAnimatedReply) {
                    (layoutParams as SmartReplyView.LayoutParams).mButtonType =
                        SmartButtonType.ANIMATED_REPLY
                } else {
                    (layoutParams as SmartReplyView.LayoutParams).mButtonType =
                        SmartButtonType.REPLY
                }
            }
    }

+39 −8
Original line number Diff line number Diff line
package com.android.systemui.statusbar.policy;

import static java.lang.Float.NaN;

import com.android.systemui.Flags;
import android.annotation.ColorInt;
import android.app.Notification;
import android.app.PendingIntent;
@@ -242,14 +242,42 @@ public class SmartReplyView extends ViewGroup {
                0 /* maxChildHeight */);
        int displayedChildCount = 0;

        // Set up a list of suggestions where actions come before replies. Note that the Buttons
        // themselves have already been added to the view hierarchy in an order such that Smart
        // Replies are shown before Smart Actions. The order of the list below determines which
        // suggestions will be shown at all - only the first X elements are shown (where X depends
        // on how much space each suggestion button needs).
        // Determine which smart suggestions (replies and actions of various types)
        // can be displayed within the available space.
        //
        // A prioritized list of all potential suggestion buttons (`smartSuggestions`) is
        // constructed to define the *selection priority* for fitting buttons onto a single line.
        // When `Flags.notificationAnimatedActionsTreatment()` is enabled, this selection
        // priority is:
        //   1. Animated Replies
        //   2. Animated Actions
        //   3. Standard Actions
        //   4. Standard Replies
        // Otherwise, if the flag is disabled, the selection priority is:
        //   1. Standard Actions
        //   2. Standard Replies
        //
        // Buttons are iterated in this selection priority order. If a button fits (possibly after
        // squeezing preceding, lower-priority, single-line buttons that are candidates for
        // squeezing),
        // it's marked for display by setting its `LayoutParams.show = true`.
        //
        // The final left-to-right visual layout of the *displayed* buttons is determined by
        // their initial sequence when added to this ViewGroup via `addPreInflatedButtons`.
        // Refer to that method and the `SmartReplyStateInflater` for how that initial
        // button order is established.
        List<View> smartSuggestions = new ArrayList<>();
        List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
        List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
        List<View> smartSuggestions = new ArrayList<>(smartActions);

        if (Flags.notificationAnimatedActionsTreatment()) {
            List<View> animatedReplies = filterActionsOrReplies(SmartButtonType.ANIMATED_REPLY);
            List<View> animatedActions = filterActionsOrReplies(SmartButtonType.ANIMATED_ACTION);
            smartSuggestions.addAll(animatedReplies);
            smartSuggestions.addAll(animatedActions);
        }

        smartSuggestions.addAll(smartActions);
        smartSuggestions.addAll(smartReplies);
        List<View> coveredSuggestions = new ArrayList<>();

@@ -764,7 +792,10 @@ public class SmartReplyView extends ViewGroup {

    enum SmartButtonType {
        REPLY,
        ACTION
        ACTION,
        ANIMATED_ACTION,
        ANIMATED_REPLY

    }

    @VisibleForTesting
+342 −87
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar.policy;

import static android.view.View.MeasureSpec;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -117,13 +116,20 @@ public class SmartReplyViewTest extends SysuiTestCase {
    private SmartActionInflaterImpl mSmartActionInflater;
    private KeyguardDismissUtil mKeyguardDismissUtil;

    @Mock private SmartReplyConstants mConstants;
    @Mock private ActivityStarter mActivityStarter;
    @Mock private HeadsUpManager mHeadsUpManager;
    @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
    @Mock private SmartReplyController mSmartReplyController;
    @Mock private  KeyguardStateController mKeyguardStateController;
    @Mock private  SysuiStatusBarStateController mStatusBarStateController;
    @Mock
    private SmartReplyConstants mConstants;
    @Mock
    private ActivityStarter mActivityStarter;
    @Mock
    private HeadsUpManager mHeadsUpManager;
    @Mock
    private NotificationRemoteInputManager mNotificationRemoteInputManager;
    @Mock
    private SmartReplyController mSmartReplyController;
    @Mock
    private KeyguardStateController mKeyguardStateController;
    @Mock
    private SysuiStatusBarStateController mStatusBarStateController;

    @Before
    public void setUp() {
@@ -207,7 +213,9 @@ public class SmartReplyViewTest extends SysuiTestCase {
        mKeyguardDismissUtil = new KeyguardDismissUtil(
                mKeyguardStateController, mStatusBarStateController, mActivityStarter) {
            public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
                    boolean requiresShadeOpen, boolean afterKeyguardGone) { }};
                    boolean requiresShadeOpen, boolean afterKeyguardGone) {
            }
        };
        mSmartReplyInflater = new SmartReplyInflaterImpl(
                mConstants,
                mKeyguardDismissUtil,
@@ -623,12 +631,12 @@ public class SmartReplyViewTest extends SysuiTestCase {

    private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
        setSmartRepliesAndActions(choices, actionTitles, false /* fromAssistant */,
                true /* useDelayedOnClickListener */);
                true /* useDelayedOnClickListener */, false /* animatedAction */);
    }

    private void setSmartRepliesAndActions(
            CharSequence[] choices, String[] actionTitles, boolean fromAssistant,
            boolean useDelayedOnClickListener) {
            boolean useDelayedOnClickListener, boolean animatedAction) {
        mView.resetSmartSuggestions(mContainer);
        Sequence<Button> inflatedReplies = SequencesKt.asSequence(
                inflateSmartReplies(choices, fromAssistant, useDelayedOnClickListener)
@@ -637,15 +645,37 @@ public class SmartReplyViewTest extends SysuiTestCase {
                createActions(actionTitles), fromAssistant);
        Sequence<Button> inflatedSmartActions = SequencesKt.asSequence(
                IntStream.range(0, smartActions.actions.size())
                        .mapToObj(idx -> mSmartActionInflater.inflateActionButton(
                        .mapToObj(idx -> {
                            Button actionButton = mSmartActionInflater.inflateActionButton(
                                    mView,
                                    mEntry,
                                    smartActions,
                                    idx,
                                    smartActions.actions.get(idx),
                                    useDelayedOnClickListener,
                                getContext()))
                                    getContext());

                            if (actionButton != null && animatedAction) {
                                ViewGroup.LayoutParams params = actionButton.getLayoutParams();
                                SmartReplyView.LayoutParams srvLayoutParams;

                                if (params instanceof SmartReplyView.LayoutParams) {
                                    srvLayoutParams = (SmartReplyView.LayoutParams) params;
                                } else if (params != null) {
                                    srvLayoutParams = (SmartReplyView.LayoutParams) mView.generateLayoutParams(params);
                                    actionButton.setLayoutParams(srvLayoutParams);
                                } else {
                                    srvLayoutParams = (SmartReplyView.LayoutParams) mView.generateDefaultLayoutParams();
                                    actionButton.setLayoutParams(srvLayoutParams); // Apply the new LayoutParams
                                }
                                // Set the button type to make it animated action
                                srvLayoutParams.mButtonType = SmartReplyView.SmartButtonType.ANIMATED_ACTION;
                            }

                            return actionButton;
                        })
                        .iterator());

        mView.addPreInflatedButtons(
                SequencesKt.toList(SequencesKt.plus(inflatedReplies, inflatedSmartActions)));
        mView.setSmartRepliesGeneratedByAssistant(fromAssistant);
@@ -728,6 +758,14 @@ public class SmartReplyViewTest extends SysuiTestCase {
        assertEqualPadding(expected, actual);
    }

    private static void assertAnimatedActionButtonShownWithEqualMeasures(
            View expected, View actual, int index) {
        assertReplyButtonShownWithEqualMeasures(expected, actual);
        SmartReplyView.LayoutParams params =
                (SmartReplyView.LayoutParams) actual.getLayoutParams();
        assertEquals("Button " + index + " should be of type ANIMATED_ACTION",
                SmartReplyView.SmartButtonType.ANIMATED_ACTION, params.mButtonType);
    }
    private static void assertReplyButtonShown(View view) {
        assertTrue(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
    }
@@ -1205,7 +1243,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */,
                false /* not animated action */);
        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        // 395, 168
@@ -1231,7 +1270,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */,
                false /* not animated action */);
        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualMeasures(expectedView, mView);
@@ -1261,7 +1301,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
                choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */,
                false /* not animated action */);
        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualMeasures(expectedView, mView);
@@ -1332,4 +1373,218 @@ public class SmartReplyViewTest extends SysuiTestCase {
        assertReplyButtonShownWithEqualMeasures(
                expectedView.getChildAt(3), mView.getChildAt(3)); // a4
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedReplies_areShownAndMeasuredCorrectly() {

        // Define 3 animated replies
        CharSequence animatedChoice0 = addAnnotationToChoice("animated_reply0", true, "attr0");
        CharSequence animatedChoice1 = addAnnotationToChoice("animated_reply1", true, "attr1");
        CharSequence animatedChoice2 = addAnnotationToChoice("animated_reply2", true, "attr2");

        CharSequence[] choicesToDisplayAndExpect =
                new CharSequence[]{animatedChoice0, animatedChoice1, animatedChoice2};
        String[] actions = new String[]{};

        ViewGroup expectedView = buildExpectedView(choicesToDisplayAndExpect, 1 /* lineCount */);
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choicesToDisplayAndExpect,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                false /* not animated action */);

        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualLayouts(expectedView, mView);
        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedActions_areShownAndMeasuredCorrectly() {

        // Define 3 animated actions
        String animatedAction0 = "animated_action0";
        String animatedAction1 = "animated_action1";
        String animatedAction2 = "animated_action2";

        String[] actions = new String[]{animatedAction0, animatedAction1, animatedAction2};
        CharSequence[] choices = new CharSequence[]{};

        ViewGroup expectedView = buildExpectedView(choices, 1 /* lineCount */,
                createActions(actions));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choices,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                true /* animated action */);

        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualMeasures(expectedView, mView);
        assertEqualLayouts(expectedView, mView);
        assertAnimatedActionButtonShownWithEqualMeasures(
                expectedView.getChildAt(0), mView.getChildAt(0), 0);
        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));

        assertAnimatedActionButtonShownWithEqualMeasures(
                expectedView.getChildAt(1), mView.getChildAt(1), 1);
        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));

        assertAnimatedActionButtonShownWithEqualMeasures(
                expectedView.getChildAt(2), mView.getChildAt(2), 2);
        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));

    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedRepliesAndActions_showAllAnimatedRepliesAndHideActions() {

        // Define 2 animated replies
        CharSequence animatedChoice0 = addAnnotationToChoice("animated_reply0", true, "attr0");
        CharSequence animatedChoice1 = addAnnotationToChoice("animated_reply1", true, "attr1");

        CharSequence[] animatedChoices = new CharSequence[]{animatedChoice0, animatedChoice1};
        CharSequence[] allChoices =
                new CharSequence[]{"A Normal Smart Reply", animatedChoice0, animatedChoice1};
        String[] actions = new String[]{"action1", "action2", "action3"};

        // We expect to show all animated replies and no actions
        ViewGroup expectedView = buildExpectedView(animatedChoices, 1 /* lineCount */,
                createActions(new String[]{"action1"}));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                allChoices,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                false /* not animated action */);

        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualLayouts(expectedView, mView);
        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(1));
        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(0));
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedRepliesAndAnimatedActions_prioritizeAnimatedReplies() {

        // Define 2 animated replies
        CharSequence animatedChoice0 = addAnnotationToChoice("animated_reply0", true, "attr0");
        CharSequence animatedChoice1 = addAnnotationToChoice("animated_reply1", true, "attr1");

        CharSequence[] animatedChoices = new CharSequence[]{animatedChoice0, animatedChoice1};
        CharSequence[] allChoices =
                new CharSequence[]{"A Normal Smart Reply", animatedChoice0, animatedChoice1};
        String[] actions = new String[]{"action1", "action2", "action3"};

        // We expect to show all animated replies and no actions
        ViewGroup expectedView = buildExpectedView(animatedChoices, 1 /* lineCount */,
                createActions(new String[]{"action1"}));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                allChoices,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                true /* animated action */);

        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        assertEqualLayouts(expectedView, mView);
        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(1));
        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(3));
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedRepliesAndActions_notEnoughSpace_showOnlyAnimatedReplies() {

        // Define 3 animated replies
        CharSequence animatedChoice0 = addAnnotationToChoice("animated_reply0", true, "attr0");
        CharSequence animatedChoice1 = addAnnotationToChoice("animated_reply1", true, "attr1");
        CharSequence animatedChoice2 = addAnnotationToChoice("animated_reply2", true, "attr2");

        CharSequence[] allChoices =
                new CharSequence[]{"A Normal Smart Reply", animatedChoice0, animatedChoice1,
                        animatedChoice2};
        String[] actions = new String[]{"action1", "action2", "action3"};

        // We expect to show only 2 animated replies
        CharSequence[] expectedAnimatedChoices =
                new CharSequence[]{animatedChoice0, animatedChoice1,
                        animatedChoice2};
        ViewGroup expectedView = buildExpectedView(expectedAnimatedChoices, 1 /* lineCount */);
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                allChoices,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                false /* not animated action */);

        mView.measure(
                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                MeasureSpec.UNSPECIFIED);

        assertEqualLayouts(expectedView, mView);
        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT)
    public void testMeasure_animatedActionsAndSmartReplies_showAllAnimatedActionsAndHideReplies() {

        // Define 3 animated actions
        String animatedAction0 = "animated_action0";
        String animatedAction1 = "animated_action1";
        String animatedAction2 = "animated_action1";

        String[] actions = new String[]{animatedAction0, animatedAction1, animatedAction2};
        CharSequence[] choices = new CharSequence[]{"reply1", "reply2", "reply3"};


        // We expect to show all animated actions and no replies
        ViewGroup expectedView = buildExpectedView(new CharSequence[]{}, 1 /* lineCount */,
                createActions(actions));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        setSmartRepliesAndActions(
                choices,
                actions,
                true /* fromAssistant */,
                true /* useDelayedOnClickListener */,
                true /* animated action */);

        mView.measure(
                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                MeasureSpec.UNSPECIFIED);

        assertEqualMeasures(expectedView, mView);
        assertEqualLayouts(expectedView, mView);

        // Verify that all replies are hidden
        assertReplyButtonHidden(mView.getChildAt(0));
        assertReplyButtonHidden(mView.getChildAt(1));
        assertReplyButtonHidden(mView.getChildAt(2));
    }
}