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

Commit c9acf678 authored by Tony Mak's avatar Tony Mak
Browse files

Allow NotificationAssistantService to suggest smart replies

If the apps has provided their own choices, they will be used, as opposed
to the "smart replies" from NAS.
Otherwise, smart replies will be applied to the notifications
with a freeform RemoteInput but without choices.

The smart reply model is not ready yet, so canned response is hardcoded
and it is disabled by default. To test it out, run
adb shell setprop persist.sys.smart_replies_experiment true
Also, to get rid of the target >= P SDK requirement, you may want to run:
adb shell settings put global smart_replies_in_notifications_flags enabled=true,max_squeeze_remeasure_attempts=3,requires_targeting_p=false

Test: atest SystemUITests
Test: atest frameworks/base/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
Test:
1. adb shell setprop persist.sys.smart_replies_experiment true
2. adb shell settings put global smart_replies_in_notifications_flags enabled=true,max_squeeze_remeasure_attempts=3,requires_targeting_p=false
3. Send a message to myself, observe the hardcoded smart replies.

Bug: 111674540

Change-Id: Ia61a77faef7c4dcba0501abfec80e3e8cc7274e4
parent 9a64ba26
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4665,6 +4665,7 @@ package android.service.notification {
    field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
    field public static final java.lang.String KEY_PEOPLE = "key_people";
    field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
    field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
    field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
  }
+1 −0
Original line number Diff line number Diff line
@@ -1044,6 +1044,7 @@ package android.service.notification {
    field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
    field public static final java.lang.String KEY_PEOPLE = "key_people";
    field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
    field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
    field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
  }
+6 −0
Original line number Diff line number Diff line
@@ -70,6 +70,12 @@ public final class Adjustment implements Parcelable {
     */
    public static final String KEY_SMART_ACTIONS = "key_smart_actions";

    /**
     * Data type: ArrayList of {@link CharSequence}.
     * Used to suggest smart replies for a notification.
     */
    public static final String KEY_SMART_REPLIES = "key_smart_replies";

    /**
     * Create a notification adjustment.
     *
+32 −2
Original line number Diff line number Diff line
@@ -1427,6 +1427,7 @@ public abstract class NotificationListenerService extends Service {
        private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
        private boolean mHidden;
        private ArrayList<Notification.Action> mSmartActions;
        private ArrayList<CharSequence> mSmartReplies;

        public Ranking() {}

@@ -1563,6 +1564,13 @@ public abstract class NotificationListenerService extends Service {
            return mSmartActions;
        }

        /**
         * @hide
         */
        public List<CharSequence> getSmartReplies() {
            return mSmartReplies;
        }

        /**
         * Returns whether this notification can be displayed as a badge.
         *
@@ -1591,7 +1599,8 @@ public abstract class NotificationListenerService extends Service {
                CharSequence explanation, String overrideGroupKey,
                NotificationChannel channel, ArrayList<String> overridePeople,
                ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
                int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions) {
                int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions,
                ArrayList<CharSequence> smartReplies) {
            mKey = key;
            mRank = rank;
            mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1608,6 +1617,7 @@ public abstract class NotificationListenerService extends Service {
            mUserSentiment = userSentiment;
            mHidden = hidden;
            mSmartActions = smartActions;
            mSmartReplies = smartReplies;
        }

        /**
@@ -1658,6 +1668,7 @@ public abstract class NotificationListenerService extends Service {
        private ArrayMap<String, Integer> mUserSentiment;
        private ArrayMap<String, Boolean> mHidden;
        private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
        private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;

        private RankingMap(NotificationRankingUpdate rankingUpdate) {
            mRankingUpdate = rankingUpdate;
@@ -1686,7 +1697,8 @@ public abstract class NotificationListenerService extends Service {
                    getVisibilityOverride(key), getSuppressedVisualEffects(key),
                    getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                    getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
                    getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key));
                    getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key),
                    getSmartReplies(key));
            return rank >= 0;
        }

@@ -1833,6 +1845,15 @@ public abstract class NotificationListenerService extends Service {
            return mSmartActions.get(key);
        }

        private ArrayList<CharSequence> getSmartReplies(String key) {
            synchronized (this) {
                if (mSmartReplies == null) {
                    buildSmartReplies();
                }
            }
            return mSmartReplies.get(key);
        }

        // Locked by 'this'
        private void buildRanksLocked() {
            String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1959,6 +1980,15 @@ public abstract class NotificationListenerService extends Service {
            }
        }

        // Locked by 'this'
        private void buildSmartReplies() {
            Bundle smartReplies = mRankingUpdate.getSmartReplies();
            mSmartReplies = new ArrayMap<>(smartReplies.size());
            for (String key : smartReplies.keySet()) {
                mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
            }
        }

        // ----------- Parcelable

        @Override
+10 −1
Original line number Diff line number Diff line
@@ -38,12 +38,14 @@ public class NotificationRankingUpdate implements Parcelable {
    private final Bundle mUserSentiment;
    private final Bundle mHidden;
    private final Bundle mSmartActions;
    private final Bundle mSmartReplies;

    public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
            Bundle visibilityOverrides, Bundle suppressedVisualEffects,
            int[] importance, Bundle explanation, Bundle overrideGroupKeys,
            Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
            Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions) {
            Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
            Bundle smartReplies) {
        mKeys = keys;
        mInterceptedKeys = interceptedKeys;
        mVisibilityOverrides = visibilityOverrides;
@@ -58,6 +60,7 @@ public class NotificationRankingUpdate implements Parcelable {
        mUserSentiment = userSentiment;
        mHidden = hidden;
        mSmartActions = smartActions;
        mSmartReplies = smartReplies;
    }

    public NotificationRankingUpdate(Parcel in) {
@@ -76,6 +79,7 @@ public class NotificationRankingUpdate implements Parcelable {
        mUserSentiment = in.readBundle();
        mHidden = in.readBundle();
        mSmartActions = in.readBundle();
        mSmartReplies = in.readBundle();
    }

    @Override
@@ -99,6 +103,7 @@ public class NotificationRankingUpdate implements Parcelable {
        out.writeBundle(mUserSentiment);
        out.writeBundle(mHidden);
        out.writeBundle(mSmartActions);
        out.writeBundle(mSmartReplies);
    }

    public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -167,4 +172,8 @@ public class NotificationRankingUpdate implements Parcelable {
    public Bundle getSmartActions() {
        return mSmartActions;
    }

    public Bundle getSmartReplies() {
        return mSmartReplies;
    }
}
Loading