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

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

Add Smart Suggestions to heads up notifications.

Add smart replies and actions to Heads-up notifications (HUN).

Note: only one line of text is shown in HUNs with messaging templates.
Thus the user might not see the full message to respond to even when the
smart replies/actions are displayed.

Screenshot: https://screenshot.googleplex.com/3R5GVZGXGNg.png

Bug: 117257685
Test: Use cinek@'s Notify app to display HUN with smart replies/actions.
Change-Id: I10d2c87b445de8f471ad0978829cede9ac6a6663
parent 451db485
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -1098,6 +1098,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        mHeadsUpManager = headsUpManager;
        mHeadsUpManager = headsUpManager;
    }
    }


    public HeadsUpManager getHeadsUpManager() {
        return mHeadsUpManager;
    }

    public void setGutsView(MenuItem item) {
    public void setGutsView(MenuItem item) {
        if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
        if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
            ((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
            ((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
+13 −1
Original line number Original line Diff line number Diff line
@@ -91,6 +91,7 @@ public class NotificationContentView extends FrameLayout {


    private SmartReplyConstants mSmartReplyConstants;
    private SmartReplyConstants mSmartReplyConstants;
    private SmartReplyView mExpandedSmartReplyView;
    private SmartReplyView mExpandedSmartReplyView;
    private SmartReplyView mHeadsUpSmartReplyView;
    private SmartReplyController mSmartReplyController;
    private SmartReplyController mSmartReplyController;


    private NotificationViewWrapper mContractedWrapper;
    private NotificationViewWrapper mContractedWrapper;
@@ -253,6 +254,9 @@ public class NotificationContentView extends FrameLayout {
        }
        }
        if (mHeadsUpChild != null) {
        if (mHeadsUpChild != null) {
            int maxHeight = mHeadsUpHeight;
            int maxHeight = mHeadsUpHeight;
            if (mHeadsUpSmartReplyView != null) {
                maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
            }
            maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
            maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
            int size = maxHeight;
            int size = maxHeight;
            ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
            ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
@@ -955,6 +959,9 @@ public class NotificationContentView extends FrameLayout {
        if (mExpandedSmartReplyView != null) {
        if (mExpandedSmartReplyView != null) {
            mExpandedSmartReplyView.setBackgroundTintColor(color);
            mExpandedSmartReplyView.setBackgroundTintColor(color);
        }
        }
        if (mHeadsUpSmartReplyView != null) {
            mHeadsUpSmartReplyView.setBackgroundTintColor(color);
        }
    }
    }


    public int getVisibleType() {
    public int getVisibleType() {
@@ -1472,6 +1479,10 @@ public class NotificationContentView extends FrameLayout {
                        entry, smartRepliesAndActions.smartReplies.choices.length);
                        entry, smartRepliesAndActions.smartReplies.choices.length);
            }
            }
        }
        }
        if (mHeadsUpChild != null) {
            mHeadsUpSmartReplyView =
                    applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
        }
    }
    }


    private SmartReplyView applySmartReplyView(View view,
    private SmartReplyView applySmartReplyView(View view,
@@ -1520,7 +1531,8 @@ public class NotificationContentView extends FrameLayout {
            }
            }
            if (smartRepliesAndActions.smartActions != null) {
            if (smartRepliesAndActions.smartActions != null) {
                smartReplyView.addSmartActions(
                smartReplyView.addSmartActions(
                        smartRepliesAndActions.smartActions, mSmartReplyController, entry);
                        smartRepliesAndActions.smartActions, mSmartReplyController, entry,
                        mContainingNotification.getHeadsUpManager());
            }
            }
            smartReplyContainer.setVisibility(View.VISIBLE);
            smartReplyContainer.setVisibility(View.VISIBLE);
        }
        }
+18 −5
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@ import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Layout;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextPaint;
import android.text.method.TransformationMethod;
import android.text.method.TransformationMethod;
@@ -61,6 +63,7 @@ public class SmartReplyView extends ViewGroup {


    private final SmartReplyConstants mConstants;
    private final SmartReplyConstants mConstants;
    private final KeyguardDismissUtil mKeyguardDismissUtil;
    private final KeyguardDismissUtil mKeyguardDismissUtil;
    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());


    /**
    /**
     * The upper bound for the height of this view in pixels. Notifications are automatically
     * The upper bound for the height of this view in pixels. Notifications are automatically
@@ -209,13 +212,15 @@ public class SmartReplyView extends ViewGroup {
     * notification are shown.
     * notification are shown.
     */
     */
    public void addSmartActions(SmartActions smartActions,
    public void addSmartActions(SmartActions smartActions,
            SmartReplyController smartReplyController, NotificationData.Entry entry) {
            SmartReplyController smartReplyController, NotificationData.Entry entry,
            HeadsUpManager headsUpManager) {
        int numSmartActions = smartActions.actions.size();
        int numSmartActions = smartActions.actions.size();
        for (int n = 0; n < numSmartActions; n++) {
        for (int n = 0; n < numSmartActions; n++) {
            Notification.Action action = smartActions.actions.get(n);
            Notification.Action action = smartActions.actions.get(n);
            if (action.actionIntent != null) {
            if (action.actionIntent != null) {
                Button actionButton = inflateActionButton(
                Button actionButton = inflateActionButton(
                        getContext(), this, n, smartActions, smartReplyController, entry);
                        getContext(), this, n, smartActions, smartReplyController, entry,
                        headsUpManager);
                addView(actionButton);
                addView(actionButton);
            }
            }
        }
        }
@@ -274,7 +279,7 @@ public class SmartReplyView extends ViewGroup {
    @VisibleForTesting
    @VisibleForTesting
    Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
    Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
            SmartActions smartActions, SmartReplyController smartReplyController,
            SmartActions smartActions, SmartReplyController smartReplyController,
            NotificationData.Entry entry) {
            NotificationData.Entry entry, HeadsUpManager headsUpManager) {
        Notification.Action action = smartActions.actions.get(actionIndex);
        Notification.Action action = smartActions.actions.get(actionIndex);
        Button button = (Button) LayoutInflater.from(context).inflate(
        Button button = (Button) LayoutInflater.from(context).inflate(
                R.layout.smart_action_button, root, false);
                R.layout.smart_action_button, root, false);
@@ -290,8 +295,12 @@ public class SmartReplyView extends ViewGroup {
        button.setOnClickListener(view ->
        button.setOnClickListener(view ->
                getActivityStarter().startPendingIntentDismissingKeyguard(
                getActivityStarter().startPendingIntentDismissingKeyguard(
                        action.actionIntent,
                        action.actionIntent,
                        () -> smartReplyController.smartActionClicked(
                        () -> {
                                entry, actionIndex, action, smartActions.fromAssistant)));
                            smartReplyController.smartActionClicked(
                                    entry, actionIndex, action, smartActions.fromAssistant);
                            postOnUiThread(() ->
                                    headsUpManager.removeNotification(entry.key, true));
                        }));


        // TODO(b/119010281): handle accessibility
        // TODO(b/119010281): handle accessibility


@@ -301,6 +310,10 @@ public class SmartReplyView extends ViewGroup {
        return button;
        return button;
    }
    }


    private void postOnUiThread(Runnable runnable) {
        mMainThreadHandler.post(runnable);
    }

    @Override
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(mContext, attrs);
        return new LayoutParams(mContext, attrs);
+6 −3
Original line number Original line Diff line number Diff line
@@ -98,6 +98,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
    private Notification mNotification;
    private Notification mNotification;


    @Mock ActivityStarter mActivityStarter;
    @Mock ActivityStarter mActivityStarter;
    @Mock HeadsUpManager mHeadsUpManager;


    @Before
    @Before
    public void setUp() {
    public void setUp() {
@@ -434,7 +435,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
        mView.addSmartActions(
        mView.addSmartActions(
                new SmartReplyView.SmartActions(createActions(actionTitles), false),
                new SmartReplyView.SmartActions(createActions(actionTitles), false),
                mLogger,
                mLogger,
                mEntry);
                mEntry,
                mHeadsUpManager);
    }
    }


    private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
    private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
@@ -442,7 +444,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
        mView.addSmartActions(
        mView.addSmartActions(
                new SmartReplyView.SmartActions(createActions(actionTitles), false),
                new SmartReplyView.SmartActions(createActions(actionTitles), false),
                mLogger,
                mLogger,
                mEntry);
                mEntry,
                mHeadsUpManager);
    }
    }


    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -747,7 +750,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
    private Button inflateActionButton(Notification.Action action) {
    private Button inflateActionButton(Notification.Action action) {
        return mView.inflateActionButton(getContext(), mView, 0,
        return mView.inflateActionButton(getContext(), mView, 0,
                new SmartReplyView.SmartActions(Collections.singletonList(action), false),
                new SmartReplyView.SmartActions(Collections.singletonList(action), false),
                mLogger, mEntry);
                mLogger, mEntry, mHeadsUpManager);
    }
    }


    @Test
    @Test