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

Commit 6daed618 authored by Ibrahim Yilmaz's avatar Ibrahim Yilmaz
Browse files

[Inline Reply] Expand Notification on inline reply

activateRemoteInputOnExpanded is new activateRemoteInput, which expands Notification before activating remote input when it is not expanded

Bug: 346976443
Test: StatusBarRemoteInputCallbackTest and manual. Send HUN Notification with Notify and click reply. Observe inline reply activated on expanded notification.
Flag: com.android.systemui.expand_heads_up_on_inline_reply
Change-Id: If6df50c987d9ee1d6c088845619d631be1a7c789
parent f6476051
Loading
Loading
Loading
Loading
+88 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry.
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
@@ -420,6 +421,12 @@ public class NotificationRemoteInputManager implements CoreStartable {
            PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
            @Nullable String userMessageContent,
            @Nullable AuthBypassPredicate authBypassCheck) {
        if (ExpandHeadsUpOnInlineReply.isEnabled()) {
            return activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
                    editedSuggestionInfo, userMessageContent,
                    authBypassCheck);
        }

        ViewParent p = view.getParent();
        RemoteInputView riv = null;
        ExpandableNotificationRow row = null;
@@ -491,6 +498,86 @@ public class NotificationRemoteInputManager implements CoreStartable {
        return true;
    }

    /**
     * Activates a given {@link RemoteInput} on the expanded notification.
     * If the given notification is not expanded, this method will expand the notification
     * first and after that activate remote input on the expanded.
     * @param view The view of the action button or suggestion chip that was tapped.
     * @param inputs The remote inputs that need to be sent to the app.
     * @param input The remote input that needs to be activated.
     * @param pendingIntent The pending intent to be sent to the app.
     * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
     *         {@code null} if the user is not editing a smart reply.
     * @param userMessageContent User-entered text with which to initialize the remote input view.
     * @param authBypassCheck Optional auth bypass check associated with this remote input
     *         activation. If {@code null}, we never bypass.
     * @return Whether the {@link RemoteInput} was activated.
     */
    public boolean activateRemoteInputOnExpanded(View view, RemoteInput[] inputs, RemoteInput input,
            PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
            @Nullable String userMessageContent,
            @Nullable AuthBypassPredicate authBypassCheck) {
        ViewParent p = view.getParent();
        RemoteInputView riv = null;
        ExpandableNotificationRow row = null;
        while (p != null) {
            if (p instanceof View) {
                View pv = (View) p;
                if (pv.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
                    row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view);
                    break;
                }
            }
            p = p.getParent();
        }

        if (row == null) {
            return false;
        }

        final boolean deferBouncer = authBypassCheck != null;
        if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
            return true;
        }

        if (!row.getPrivateLayout().getExpandedChild().isShown()) {
            // The expanded layout is selected, but it's not shown yet, let's wait on it to
            // show before we do the animation.
            mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
                activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
                        editedSuggestionInfo, userMessageContent, authBypassCheck);
            });
            return true;
        }

        riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
        if (riv == null) {
            return false;
        }

        if (!riv.isAttachedToWindow()) {
            // if we still didn't find a view that is attached, let's abort.
            return false;
        }

        riv.getController().setPendingIntent(pendingIntent);
        riv.getController().setRemoteInput(input);
        riv.getController().setRemoteInputs(inputs);
        riv.getController().setEditedSuggestionInfo(editedSuggestionInfo);
        riv.focusAnimated();
        if (userMessageContent != null) {
            riv.setEditTextContent(userMessageContent);
        }
        if (deferBouncer) {
            final ExpandableNotificationRow finalRow = row;
            riv.getController().setBouncerChecker(() ->
                    !authBypassCheck.canSendRemoteInputWithoutBouncer()
                            && showBouncerForRemoteInput(view, pendingIntent, finalRow));
        }

        return true;
    }

    private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
            ExpandableNotificationRow row) {

+48 −32
Original line number Diff line number Diff line
@@ -329,13 +329,27 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
    private OnClickListener mExpandClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            toggleExpansionState(v, /* shouldLogExpandClickMetric = */true);
        }
    };

    /**
     * Toggles expansion state.
     */
    public void toggleExpansionState() {
        toggleExpansionState(this, /*shouldLogExpandClickMetric*/ false);
    }

    private void toggleExpansionState(View v, boolean shouldLogExpandClickMetric) {
        if (!shouldShowPublic() && (!mIsMinimized || isExpanded())
                && mGroupMembershipManager.isGroupSummary(mEntry)) {
            mGroupExpansionChanging = true;
            final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
            boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
            mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
            if (shouldLogExpandClickMetric) {
                mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
            }
            onExpansionChanged(true /* userAction */, wasExpanded);
        } else if (mEnableNonGroupedNotificationExpand) {
            if (v.isAccessibilityFocused()) {
@@ -364,10 +378,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView

            notifyHeightChanged(/* needsAnimation= */ true);
            mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
            if (shouldLogExpandClickMetric) {
                mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
            }
        }
    };
    }

    private boolean mKeepInParentForDismissAnimation;
    private boolean mRemoved;
    public static final FloatProperty<ExpandableNotificationRow> TRANSLATE_CONTENT =
+32 −20
Original line number Diff line number Diff line
@@ -191,6 +191,16 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
            View clickedView, boolean deferBouncer, Runnable runnable) {
        if (!deferBouncer && mKeyguardStateController.isShowing()) {
            onLockedRemoteInput(row, clickedView);
        } else {
            if (ExpandHeadsUpOnInlineReply.isEnabled()) {
                if (row.isChildInGroup() && !row.areChildrenExpanded()) {
                    // The group isn't expanded, let's make sure it's visible!
                    mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
                } else if (!row.isChildInGroup() && !row.isExpanded()) {
                    // notification isn't expanded, let's make sure it's visible!
                    row.toggleExpansionState();
                    row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
                }
            } else {
                if (row.isChildInGroup() && !row.areChildrenExpanded()) {
                    // The group isn't expanded, let's make sure it's visible!
@@ -200,8 +210,9 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
                if (android.app.Flags.compactHeadsUpNotificationReply()
                        && row.isCompactConversationHeadsUpOnScreen()) {
                    // Notification can be system expanded true and it is set user expanded in
                // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
                // change. To expand huning notification properly, we need set userExpanded false.
                    // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType
                    // doesn't change. To expand huning notification properly,
                    // we need set userExpanded false.
                    if (!row.isPinned() && row.isExpanded()) {
                        row.setUserExpanded(false);
                    }
@@ -218,6 +229,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
                row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
            }
        }
    }

    @Override
    public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
+104 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone;
import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -26,6 +27,7 @@ import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;

import android.content.Intent;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.View;

@@ -180,4 +182,106 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
        verify(enr).setUserExpanded(true);
        verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
    }

    @Test
    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
    public void onMakeExpandedVisibleForRemoteInput_notExpandedGroup_toggleExpansion() {
        // GIVEN
        final Runnable onExpandedVisibleRunner = mock(Runnable.class);

        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
        final NotificationContentView privateLayout = mock(NotificationContentView.class);
        final NotificationEntry enrEntry = mock(NotificationEntry.class);

        when(enr.getPrivateLayout()).thenReturn(privateLayout);
        when(enr.getEntry()).thenReturn(enrEntry);
        when(enr.isChildInGroup()).thenReturn(true);
        when(enr.areChildrenExpanded()).thenReturn(false);

        // WHEN
        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
                enr, mock(View.class), false, onExpandedVisibleRunner);

        // THEN
        verify(mGroupExpansionManager).toggleGroupExpansion(enrEntry);
        verify(enr, never()).setUserExpanded(anyBoolean());
        verify(privateLayout, never()).setOnExpandedVisibleListener(any());
    }

    @Test
    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
    public void onMakeExpandedVisibleForRemoteInput_expandedGroup_notToggleExpansion() {
        // GIVEN
        final Runnable onExpandedVisibleRunner = mock(Runnable.class);

        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
        final NotificationContentView privateLayout = mock(NotificationContentView.class);
        final NotificationEntry enrEntry = mock(NotificationEntry.class);

        when(enr.getPrivateLayout()).thenReturn(privateLayout);
        when(enr.getEntry()).thenReturn(enrEntry);
        when(enr.isChildInGroup()).thenReturn(true);
        when(enr.areChildrenExpanded()).thenReturn(true);

        // WHEN
        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
                enr, mock(View.class), false, onExpandedVisibleRunner);

        // THEN
        verify(mGroupExpansionManager, never()).toggleGroupExpansion(enrEntry);
        verify(enr, never()).setUserExpanded(anyBoolean());
        verify(privateLayout, never()).setOnExpandedVisibleListener(any());
    }

    @Test
    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
    public void onMakeExpandedVisibleForRemoteInput_notExpandedNotification_toggleExpansion() {
        // GIVEN
        final Runnable onExpandedVisibleRunner = mock(Runnable.class);

        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
        final NotificationContentView privateLayout = mock(NotificationContentView.class);
        final NotificationEntry enrEntry = mock(NotificationEntry.class);

        when(enr.getPrivateLayout()).thenReturn(privateLayout);
        when(enr.getEntry()).thenReturn(enrEntry);
        when(enr.isChildInGroup()).thenReturn(false);
        when(enr.isExpanded()).thenReturn(false);

        // WHEN
        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
                enr, mock(View.class), false, onExpandedVisibleRunner);

        // THEN
        verify(enr).toggleExpansionState();
        verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
        verify(enr, never()).setUserExpanded(anyBoolean());
        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
    }

    @Test
    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
    public void onMakeExpandedVisibleForRemoteInput_expandedNotification_notToggleExpansion() {
        // GIVEN
        final Runnable onExpandedVisibleRunner = mock(Runnable.class);

        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
        final NotificationContentView privateLayout = mock(NotificationContentView.class);
        final NotificationEntry enrEntry = mock(NotificationEntry.class);

        when(enr.getPrivateLayout()).thenReturn(privateLayout);
        when(enr.getEntry()).thenReturn(enrEntry);
        when(enr.isChildInGroup()).thenReturn(false);
        when(enr.isExpanded()).thenReturn(true);

        // WHEN
        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
                enr, mock(View.class), false, onExpandedVisibleRunner);

        // THEN
        verify(enr, never()).toggleExpansionState();
        verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
        verify(enr, never()).setUserExpanded(anyBoolean());
        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
    }
}