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

Commit b149a1a9 authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Prioritize Smart Actions over Smart Replies.

Up until now we've added Smart Replies and Actions prioritizing Replies
over Actions (because Replies show up before / to the left of Actions in
the UI). With this CL we prioritize adding Smart Actions over adding
Smart Replies.

Bug: 119010281
Test: atest SmartReplyViewTest
Test: manual, using cinek@'s Notify app: ensure if there are enough
smart actions to fill the notification view, no smart replies are added.

Change-Id: Ia413d6a9d3cf04cfb0dcad2df560aa0dd1660287
parent 5c4f2149
Loading
Loading
Loading
Loading
+47 −11
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;

import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
@@ -285,6 +286,9 @@ public class SmartReplyView extends ViewGroup {

        // TODO(b/119010281): handle accessibility

        // Mark this as an Action button
        final LayoutParams lp = (LayoutParams) button.getLayoutParams();
        lp.buttonType = SmartButtonType.ACTION;
        return button;
    }

@@ -342,18 +346,26 @@ public class SmartReplyView extends ViewGroup {
        int displayedChildCount = 0;
        int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
        // 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).
        List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
        List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
        List<View> smartSuggestions = new ArrayList<>(smartActions);
        smartSuggestions.addAll(smartReplies);
        List<View> coveredSuggestions = new ArrayList<>();

        for (View child : smartSuggestions) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
                continue;
            }

            child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
                    buttonPaddingHorizontal, child.getPaddingBottom());
            child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);

            coveredSuggestions.add(child);

            final int lineCount = ((Button) child).getLineCount();
            if (lineCount < 1 || lineCount > 2) {
                // If smart reply has no text, or more than two lines, then don't show it.
@@ -407,7 +419,8 @@ public class SmartReplyView extends ViewGroup {

                    // Mark all buttons from the last squeezing round as "failed to squeeze", so
                    // that they're re-measured without squeezing later.
                    markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i);
                    markButtonsWithPendingSqueezeStatusAs(
                            LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);

                    // The current button doesn't fit, so there's no point in measuring further
                    // buttons.
@@ -416,7 +429,8 @@ public class SmartReplyView extends ViewGroup {

                // The current button fits, so mark all squeezed buttons as "successfully squeezed"
                // to prevent them from being un-squeezed in a subsequent squeezing round.
                markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i);
                markButtonsWithPendingSqueezeStatusAs(
                        LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions);
            }

            lp.show = true;
@@ -435,6 +449,22 @@ public class SmartReplyView extends ViewGroup {
                        mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
    }

    private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
        List<View> actions = new ArrayList<>();
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
                continue;
            }
            if (lp.buttonType == buttonType) {
                actions.add(child);
            }
        }
        return actions;
    }

    private void resetButtonsLayoutParams() {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
@@ -605,9 +635,9 @@ public class SmartReplyView extends ViewGroup {
        }
    }

    private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) {
        for (int i = 0; i <= maxChildIndex; i++) {
            final View child = getChildAt(i);
    private void markButtonsWithPendingSqueezeStatusAs(
            int squeezeStatus, List<View> coveredChildren) {
        for (View child : coveredChildren) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) {
                lp.squeezeStatus = squeezeStatus;
@@ -702,6 +732,11 @@ public class SmartReplyView extends ViewGroup {
        return mActivityStarter;
    }

    private enum SmartButtonType {
        REPLY,
        ACTION
    }

    @VisibleForTesting
    static class LayoutParams extends ViewGroup.LayoutParams {

@@ -727,6 +762,7 @@ public class SmartReplyView extends ViewGroup {

        private boolean show = false;
        private int squeezeStatus = SQUEEZE_STATUS_NONE;
        private SmartButtonType buttonType = SmartButtonType.REPLY;

        private LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
+51 −0
Original line number Diff line number Diff line
@@ -775,4 +775,55 @@ public class SmartReplyViewTest extends SysuiTestCase {
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
    }

    @Test
    public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
        String[] choices = new String[] {"Reply"};
        String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};

        // All actions should be displayed as DOUBLE-line smart action buttons.
        ViewGroup expectedView = buildExpectedView(new String[0], 2,
                createActions(new String[] {
                        "Looooooong \nactioooon", "second \naction", "third \naction"}));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

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

        assertEqualMeasures(expectedView, mView);
        // smart replies
        assertReplyButtonHidden(mView.getChildAt(0));
        // smart actions
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
    }

    @Test
    public void testMeasure_choicesAndActionsPrioritizeActions() {
        String[] choices = new String[] {"Short", "longer reply"};
        String[] actions = new String[] {"Looooooong actioooon", "second action"};

        // All actions should be displayed as DOUBLE-line smart action buttons.
        ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
                createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

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

        Button firstAction = ((Button) mView.getChildAt(1));

        assertEqualMeasures(expectedView, mView);
        // smart replies
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
        assertReplyButtonHidden(mView.getChildAt(1));
        // smart actions
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
    }
}