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

Commit 794bd34b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update ExtService to use suggestConversationActions API to generate replies"

parents 0c9e5fe0 d287485e
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package android.ext.services.notification;

import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -148,6 +149,18 @@ public class NotificationEntry {
        return Objects.equals(getNotification().category, category);
    }

    /**
     * Similar to {@link #isCategory(String)}, but checking the public version of the notification,
     * if available.
     */
    public boolean isPublicVersionCategory(String category) {
        Notification publicVersion = getNotification().publicVersion;
        if (publicVersion == null) {
            return false;
        }
        return Objects.equals(publicVersion.category, category);
    }

    public boolean isAudioAttributesUsage(int usage) {
        return mAttributes != null && mAttributes.getUsage() == usage;
    }
@@ -175,9 +188,9 @@ public class NotificationEntry {
    }

    protected boolean isMessaging() {
        return isCategory(Notification.CATEGORY_MESSAGE)
                || hasStyle(Notification.MessagingStyle.class)
                || hasInlineReply();
        return isCategory(CATEGORY_MESSAGE)
                || isPublicVersionCategory(CATEGORY_MESSAGE)
                || hasStyle(Notification.MessagingStyle.class);
    }

    public boolean hasInlineReply() {
+53 −15
Original line number Diff line number Diff line
@@ -19,23 +19,22 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.RemoteAction;
import android.app.RemoteInput;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemProperties;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class SmartActionsHelper {
    private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
@@ -50,12 +49,18 @@ public class SmartActionsHelper {
    private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
    private static final int MAX_ACTIONS_PER_LINK = 1;
    private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
    // Allow us to test out smart reply with dumb suggestions, it is disabled by default.
    // TODO: Removed this once we have the model.
    private static final String SYS_PROP_SMART_REPLIES_EXPERIMENT =
            "persist.sys.smart_replies_experiment";
    private static final int MAX_SUGGESTED_REPLIES = 3;

    SmartActionsHelper() {}
    private static final ConversationActions.TypeConfig TYPE_CONFIG =
            new ConversationActions.TypeConfig.Builder().setIncludedTypes(
                    Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
                    .includeTypesFromTextClassifier(false)
                    .build();
    private static final List<String> HINTS =
            Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);

    SmartActionsHelper() {
    }

    /**
     * Adds action adjustments based on the notification contents.
@@ -92,8 +97,31 @@ public class SmartActionsHelper {
        if (context == null) {
            return EMPTY_REPLY_LIST;
        }
        // TODO: replaced this with our model when it is ready.
        return new ArrayList<>(Arrays.asList("Yes, please", "No, thanks"));
        TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
        if (tcm == null) {
            return EMPTY_REPLY_LIST;
        }
        CharSequence text = getMostSalientActionText(entry.getNotification());
        ConversationActions.Message message =
                new ConversationActions.Message.Builder()
                        .setText(text)
                        .build();

        ConversationActions.Request request =
                new ConversationActions.Request.Builder(Collections.singletonList(message))
                        .setMaxSuggestions(MAX_SUGGESTED_REPLIES)
                        .setHints(HINTS)
                        .setTypeConfig(TYPE_CONFIG)
                        .build();

        TextClassifier textClassifier = tcm.getTextClassifier();
        List<ConversationActions.ConversationAction> conversationActions =
                textClassifier.suggestConversationActions(request).getConversationActions();

        return conversationActions.stream()
                .map(conversationAction -> conversationAction.getTextReply())
                .filter(textReply -> !TextUtils.isEmpty(textReply))
                .collect(Collectors.toCollection(ArrayList::new));
    }

    /**
@@ -124,20 +152,30 @@ public class SmartActionsHelper {
    }

    private boolean isEligibleForReplyAdjustment(@NonNull NotificationEntry entry) {
        if (!SystemProperties.getBoolean(SYS_PROP_SMART_REPLIES_EXPERIMENT, false)) {
        if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
            return false;
        }
        Notification notification = entry.getNotification();
        if (notification.actions == null) {
        String pkg = entry.getSbn().getPackageName();
        if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
            return false;
        }
        // For now, we are only interested in messages.
        if (!entry.isMessaging()) {
            return false;
        }
        // Does not make sense to provide suggested replies if it is not something that can be
        // replied.
        if (!entry.hasInlineReply()) {
            return false;
        }
        return entry.hasInlineReply();
        return true;
    }

    /** Returns the text most salient for action extraction in a notification. */
    @Nullable
    private CharSequence getMostSalientActionText(@NonNull Notification notification) {
        /* If it's messaging style, use the most recent message. */
        // TODO: Use the last few X messages instead and take the Person object into consideration.
        Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
        if (messages != null && messages.length != 0) {
            Bundle lastMessage = (Bundle) messages[messages.length - 1];