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

Commit 7331c90a authored by Shery Sheng's avatar Shery Sheng Committed by Lucas Dupin
Browse files

Add extra to feedback action

So the NAS knows what kind of feedback the user wants to file
Show feedback icon when NmSummarizationUiFlag or notificationAnimatedActionsTreatment
is on and their feature is showing on that notification

FCRS_CODE: m9w6zm271d1unl
Test: added unit tests
Flag: android.app.nm_summarization
Fixes: 409734380
Bug: 388567770
Change-Id: I516fa0e95b1a7e022373535e9555e35154659291
parent a090aa24
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13574,6 +13574,7 @@ package android.service.notification {
    field public static final String ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS = "android.service.notification.action.NOTIFICATION_ASSISTANT_DETAIL_SETTINGS";
    field @FlaggedApi("android.service.notification.notification_classification") public static final String ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS = "android.service.notification.action.NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS";
    field @FlaggedApi("android.app.nm_summarization") public static final String EXTRA_NOTIFICATION_ADJUSTMENT = "android.service.notification.extra.NOTIFICATION_ADJUSTMENT";
    field @FlaggedApi("android.app.nm_summarization") public static final String EXTRA_NOTIFICATION_ADJUSTMENTS = "android.service.notification.extra.NOTIFICATION_ADJUSTMENTS";
    field @FlaggedApi("android.service.notification.notification_classification") public static final String EXTRA_NOTIFICATION_KEY = "android.service.notification.extra.NOTIFICATION_KEY";
    field public static final String FEEDBACK_RATING = "feedback.rating";
    field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+10 −0
Original line number Diff line number Diff line
@@ -142,6 +142,16 @@ public abstract class NotificationAssistantService extends NotificationListenerS
    public static final String EXTRA_NOTIFICATION_ADJUSTMENT
            = "android.service.notification.extra.NOTIFICATION_ADJUSTMENT";

    /**
     * A string ArrayList extra containing the {@link Adjustment} key that the user wants to file
     * feedback about.
     *
     * Extra for {@link #ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS}.
     */
    @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
    public static final String EXTRA_NOTIFICATION_ADJUSTMENTS
            = "android.service.notification.extra.NOTIFICATION_ADJUSTMENTS";

    /**
     * Data type: int, the feedback rating score provided by user. The score can be any integer
     *            value depends on the experimental and feedback UX design.
+48 −3
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.Annotation;
import android.text.Spanned;
import android.text.TextUtils;
import android.transition.ChangeBounds;
import android.transition.Fade;
@@ -81,6 +83,8 @@ import com.android.systemui.wmshell.BubblesManager;

import java.lang.annotation.Retention;
import java.util.Optional;
import java.util.ArrayList;
import java.util.List;

/**
 * The guts of a conversation notification revealed when performing a long press.
@@ -303,11 +307,52 @@ public class NotificationConversationInfo extends LinearLayout implements
        // Delegate
        bindDelegate();
    }

    private boolean showSummarizationFeedback() {
        return NmSummarizationUiFlag.isEnabled()
                && !TextUtils.isEmpty(mRanking.getSummarization());
    }
    private static boolean isAnimatedReply(CharSequence choice) {
        if (choice instanceof Spanned) {
            Spanned spanned = (Spanned) choice;
            Annotation[] annotations = spanned.getSpans(0, choice.length(), Annotation.class);
            if (annotations != null) { // Add null check
                for (Annotation annotation : annotations) {
                    if ("isAnimatedReply".equals(annotation.getKey())
                            && "1".equals(annotation.getValue())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    private boolean showAnimatedFeedback() {
        boolean hasAnimatedSmartReplies = false;
        boolean hasAnimatedSmartActions = false;
        // Check for animated smart replies
        List<CharSequence> smartReplies = mRanking.getSmartReplies();
            for (CharSequence reply : smartReplies) {
                if (isAnimatedReply(reply)) {
                    hasAnimatedSmartReplies = true;
                    break;
                }
            }
        // Check for animated actions
        List<Notification.Action> smartActions = mRanking.getSmartActions();
            for (Notification.Action action : smartActions) {
                if (action != null && action.getExtras() != null &&
                        action.getExtras().getBoolean(Notification.Action.EXTRA_IS_ANIMATED,
                                false)) {
                    hasAnimatedSmartActions = true;
                    break;
                }
            }
        return com.android.systemui.Flags.notificationAnimatedActionsTreatment() &&
                (hasAnimatedSmartActions || hasAnimatedSmartReplies);
    }
    private void bindFeedback() {
        View feedbackButton = findViewById(R.id.feedback);
        if (!NmSummarizationUiFlag.isEnabled()
                || TextUtils.isEmpty(mRanking.getSummarization())) {
        if (!showSummarizationFeedback() && !showAnimatedFeedback()) {
            feedbackButton.setVisibility(GONE);
        } else {
            Intent intent = NotificationInfo.getAssistantFeedbackIntent(
+66 −3
Original line number Diff line number Diff line
@@ -23,7 +23,9 @@ import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_TYPE;

import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
@@ -52,7 +54,9 @@ import android.os.RemoteException;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.Annotation;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.transition.ChangeBounds;
import android.transition.Fade;
@@ -82,6 +86,7 @@ import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyl
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;

import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;

/**
@@ -393,7 +398,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        View feedbackButton = findViewById(R.id.feedback);
        Intent intent = getAssistantFeedbackIntent(
                mINotificationManager, mPm, mSbn.getKey(), mRanking);
        if (!android.app.Flags.notificationClassificationUi() || intent == null) {
        if ((!android.app.Flags.notificationClassificationUi() &&
                !com.android.systemui.Flags.notificationAnimatedActionsTreatment())
                 || intent == null) {
            feedbackButton.setVisibility(GONE);
        } else {
            feedbackButton.setVisibility(VISIBLE);
@@ -404,7 +411,21 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
            });
        }
    }

    private static boolean isAnimatedReply(CharSequence choice) {
        if (choice instanceof Spanned) {
            Spanned spanned = (Spanned) choice;
            Annotation[] annotations = spanned.getSpans(0, choice.length(), Annotation.class);
            if (annotations != null) { // Add null check
                for (Annotation annotation : annotations) {
                    if ("isAnimatedReply".equals(annotation.getKey())
                            && "1".equals(annotation.getValue())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    public static Intent getAssistantFeedbackIntent(INotificationManager inm, PackageManager pm,
            String key, NotificationListenerService.Ranking ranking) {
        try {
@@ -428,6 +449,48 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
            intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_KEY, key);
            intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_ADJUSTMENT,
                    ranking.getSummarization() != null ? KEY_SUMMARIZATION : KEY_TYPE);
            ArrayList<String> keys = new ArrayList<>();
            NotificationChannel channel = ranking.getChannel(); // Get channel from ranking

            // Check for summarization
            if (!TextUtils.isEmpty(ranking.getSummarization())) {
                keys.add(KEY_SUMMARIZATION);
            }

            // Check if it's a reserved system channel type
            if (channel != null && SYSTEM_RESERVED_IDS.contains(channel.getId())) {
                keys.add(KEY_TYPE);
            }

            // Check for animated smart actions
            List<Notification.Action> smartActions = ranking.getSmartActions();
            if (smartActions != null) {
                for (Notification.Action action : smartActions) {
                    if (action != null && action.getExtras() != null &&
                            action.getExtras().getBoolean(Notification.Action.EXTRA_IS_ANIMATED,
                                    false)) {
                        keys.add(KEY_CONTEXTUAL_ACTIONS);
                        break;
                    }
                }
            }

            // Check for animated smart replies
            List<CharSequence> smartReplies = ranking.getSmartReplies();
            if (smartReplies != null) {
                for (CharSequence reply : smartReplies) {
                    if (isAnimatedReply(reply)) {
                        keys.add(KEY_TEXT_REPLIES);
                        break;
                    }
                }
            }

            if (!keys.isEmpty()) {
                intent.putStringArrayListExtra(
                        NotificationAssistantService.EXTRA_NOTIFICATION_ADJUSTMENTS, keys);
            }

            return intent;
        } catch (Exception e) {
            Slog.d(TAG, "no assistant?", e);
+12 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;

import static com.android.systemui.Flags.FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;

import static com.google.common.truth.Truth.assertThat;
@@ -436,7 +437,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
    }

    @Test
    @DisableFlags({Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI})
    @DisableFlags({
            Flags.FLAG_NM_SUMMARIZATION,
            Flags.FLAG_NM_SUMMARIZATION_UI,
            FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT
    })
    public void testBindNotification_HidesFeedbackLink_flagOff() {
        doStandardBind();
        assertEquals(GONE, mNotificationInfo.findViewById(R.id.feedback).getVisibility());
@@ -479,7 +484,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
    }

    @Test
    @EnableFlags({Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI})
    @EnableFlags({
            Flags.FLAG_NM_SUMMARIZATION,
            Flags.FLAG_NM_SUMMARIZATION_UI,
            FLAG_NOTIFICATION_ANIMATED_ACTIONS_TREATMENT
    })
    public void testBindNotification_hidesFeedbackLink_ifSummaryNotInRanking() {
        doStandardBind();