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

Commit 894d9153 authored by Gus Prevas's avatar Gus Prevas
Browse files

Adds initial action to NotificationInfo menu items.

This change modifies the mechanism for creating items in
NotificationMenuRow which open the NotificationInfo panel such that they
can optionally specify an action to take immediately - specifically,
blocking the channel or toggling its priority.

This will allow us to add buttons for these actions to the menu row on
the right side of the notification.

Bug: 116622974
Test: atest NotificationInfoTest NotificationGutsManagerTest
Change-Id: If87bd4f48907efa692810a6bfef1aa85251f3e38
parent 70b7c3df
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.service.notification.NotificationListenerService.Ranking
        .USER_SENTIMENT_NEGATIVE;

import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;

import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Context;
@@ -189,7 +191,13 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
            } else if (gutsView instanceof AppOpsInfo) {
                initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
            } else if (gutsView instanceof NotificationInfo) {
                initializeNotificationInfo(row, (NotificationInfo) gutsView);
                int action;
                if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
                    action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
                } else {
                    action = ACTION_NONE;
                }
                initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
            }
            return true;
        } catch (Exception e) {
@@ -246,14 +254,15 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx

    /**
     * Sets up the {@link NotificationInfo} inside the notification row's guts.
     *
     * @param row view to set up the guts for
     * @param notificationInfoView view to set up/bind within {@code row}
     * @param action The action to take immediately upon binding, if any.
     */
    @VisibleForTesting
    void initializeNotificationInfo(
            final ExpandableNotificationRow row,
            NotificationInfo notificationInfoView) throws Exception {
            NotificationInfo notificationInfoView,
            @NotificationInfo.NotificationInfoAction int action) throws Exception {
        NotificationGuts guts = row.getGuts();
        StatusBarNotification sbn = row.getStatusBarNotification();
        String packageName = sbn.getPackageName();
@@ -297,7 +306,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
                isForBlockingHelper,
                row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
                row.getEntry().noisy,
                row.getEntry().importance);
                row.getEntry().importance,
                action);

    }

+59 −46
Original line number Diff line number Diff line
@@ -71,16 +71,19 @@ import java.util.List;
public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
    private static final String TAG = "InfoGuts";

    @IntDef(prefix = { "SWAP_CONTENT_" }, value = {
            SWAP_CONTENT_UNDO,
            SWAP_CONTENT_TOGGLE_SILENT,
            SWAP_CONTENT_BLOCK,
    @IntDef(prefix = { "ACTION_" }, value = {
            ACTION_NONE,
            ACTION_UNDO,
            ACTION_TOGGLE_SILENT,
            ACTION_BLOCK,
    })
    @interface SwapContentAction {}
    public @interface NotificationInfoAction {
    }

    private static final int SWAP_CONTENT_UNDO = 0;
    private static final int SWAP_CONTENT_TOGGLE_SILENT = 1;
    private static final int SWAP_CONTENT_BLOCK = 2;
    public static final int ACTION_NONE = 0;
    public static final int ACTION_UNDO = 1;
    public static final int ACTION_TOGGLE_SILENT = 2;
    public static final int ACTION_BLOCK = 3;

    private INotificationManager mINotificationManager;
    private PackageManager mPm;
@@ -123,8 +126,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G

    private OnClickListener mOnToggleSilent = v -> {
        Runnable saveImportance = () -> {
            mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
            swapContent(SWAP_CONTENT_TOGGLE_SILENT);
            swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
        };
        if (mCheckSaveListener != null) {
            mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -135,8 +137,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G

    private OnClickListener mOnStopOrMinimizeNotifications = v -> {
        Runnable saveImportance = () -> {
            mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
            swapContent(SWAP_CONTENT_BLOCK);
            swapContent(ACTION_BLOCK, true /* animate */);
        };
        if (mCheckSaveListener != null) {
            mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -149,7 +150,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        // Reset exit counter that we'll log and record an undo event separately (not an exit event)
        mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
        logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
        swapContent(SWAP_CONTENT_UNDO);
        swapContent(ACTION_UNDO, true /* animate */);
    };

    public NotificationInfo(Context context, AttributeSet attrs) {
@@ -185,13 +186,14 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
            boolean isDeviceProvisioned,
            boolean isNonblockable,
            boolean isNoisy,
            int importance)
            int importance,
            @NotificationInfoAction int action)
            throws RemoteException {
        bindNotification(pm, iNotificationManager, pkg, notificationChannel,
                numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
                onAppSettingsClick, isDeviceProvisioned, isNonblockable,
                false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
                importance);
                importance, action);
    }

    public void bindNotification(
@@ -209,7 +211,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
            boolean isForBlockingHelper,
            boolean isUserSentimentNegative,
            boolean isNoisy,
            int importance)
            int importance,
            @NotificationInfoAction int action)
            throws RemoteException {
        mINotificationManager = iNotificationManager;
        mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -250,6 +253,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        bindHeader();
        bindPrompt();
        bindButtons();

        if (action != ACTION_NONE) {
            swapContent(action, false /* don't animate */);
        }
    }

    private void bindHeader() throws RemoteException {
@@ -351,7 +358,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
    }

    private void saveImportance() {
        if (!mIsNonblockable) {
        if (!mIsNonblockable
                || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
            updateImportance();
        }
    }
@@ -421,7 +429,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        }
    }

    private void swapContent(@SwapContentAction int action) {
    private void swapContent(@NotificationInfoAction int action, boolean animate) {
        if (mExpandAnimation != null) {
            mExpandAnimation.cancel();
        }
@@ -432,10 +440,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        View header = findViewById(R.id.header);

        switch (action) {
            case SWAP_CONTENT_UNDO:
            case ACTION_UNDO:
                mChosenImportance = mStartingChannelImportance;
                break;
            case SWAP_CONTENT_TOGGLE_SILENT:
            case ACTION_TOGGLE_SILENT:
                mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
                if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
                    mChosenImportance = IMPORTANCE_LOW;
                    confirmationText.setText(R.string.notification_channel_silenced);
@@ -444,7 +453,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
                    confirmationText.setText(R.string.notification_channel_unsilenced);
                }
                break;
            case SWAP_CONTENT_BLOCK:
            case ACTION_BLOCK:
                mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
                if (mIsForeground) {
                    mChosenImportance = IMPORTANCE_MIN;
                    confirmationText.setText(R.string.notification_channel_minimized);
@@ -457,7 +467,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
                throw new IllegalArgumentException();
        }

        boolean isUndo = action == SWAP_CONTENT_UNDO;
        boolean isUndo = action == ACTION_UNDO;

        prompt.setVisibility(isUndo ? VISIBLE : GONE);
        confirmation.setVisibility(isUndo ? GONE : VISIBLE);
        header.setVisibility(isUndo ? VISIBLE : GONE);

        if (animate) {
            ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
                    prompt.getAlpha(), isUndo ? 1f : 0f);
            promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
@@ -465,30 +481,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
                    confirmation.getAlpha(), isUndo ? 0f : 1f);
            confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);

        prompt.setVisibility(isUndo ? VISIBLE : GONE);
        confirmation.setVisibility(isUndo ? GONE : VISIBLE);
        header.setVisibility(isUndo ? VISIBLE : GONE);

            mExpandAnimation = new AnimatorSet();
            mExpandAnimation.playTogether(promptAnim, confirmAnim);
            mExpandAnimation.setDuration(150);
            mExpandAnimation.addListener(new AnimatorListenerAdapter() {
            boolean cancelled = false;
                boolean mCancelled = false;

                @Override
                public void onAnimationCancel(Animator animation) {
                cancelled = true;
                    mCancelled = true;
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                if (!cancelled) {
                    if (!mCancelled) {
                        prompt.setVisibility(isUndo ? VISIBLE : GONE);
                        confirmation.setVisibility(isUndo ? GONE : VISIBLE);
                    }
                }
            });
            mExpandAnimation.start();
        }

        // Since we're swapping/update the content, reset the timeout so the UI can't close
        // immediately after the update.
+18 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;

import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,6 +42,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;

import java.util.ArrayList;
@@ -610,8 +612,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
        String infoDescription = res.getString(R.string.notification_menu_gear_description);
        NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                R.layout.notification_info, null, false);
        MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent,
                R.drawable.ic_settings);
        MenuItem info = new NotificationInfoMenuItem(context, infoDescription, infoContent,
                R.drawable.ic_settings, ACTION_NONE);
        return info;
    }

@@ -737,4 +739,18 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
            return mContentDescription;
        }
    }

    /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
    public static class NotificationInfoMenuItem extends NotificationMenuItem {

        @NotificationInfoAction
        int mAction;

        public NotificationInfoMenuItem(Context context, String s,
                NotificationInfo content, int iconResId,
                @NotificationInfoAction int action) {
            super(context, s, content, iconResId);
            this.mAction = action;
        }
    }
}
+51 −10
Original line number Diff line number Diff line
@@ -284,7 +284,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
        when(row.getIsNonblockable()).thenReturn(false);
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();

        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_NONE);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
@@ -301,7 +302,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
                eq(true) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(false) /*isNoisy */,
                eq(0));
                eq(0),
                eq(NotificationInfo.ACTION_NONE));
    }

    @Test
@@ -313,7 +315,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
        when(row.getIsNonblockable()).thenReturn(false);
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();

        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_NONE);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
@@ -330,7 +333,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
                eq(false) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(false) /*isNoisy */,
                eq(0));
                eq(0),
                eq(NotificationInfo.ACTION_NONE));
    }

    @Test
@@ -343,7 +347,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
        when(row.getIsNonblockable()).thenReturn(false);
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();

        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_NONE);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
@@ -360,7 +365,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
                eq(true) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(true) /*isNoisy */,
                eq(0));
                eq(0),
                eq(NotificationInfo.ACTION_NONE));
    }

    @Test
@@ -373,7 +379,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
        when(row.getIsNonblockable()).thenReturn(false);
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();

        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_NONE);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
@@ -390,7 +397,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
                eq(true) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(false) /*isNoisy */,
                eq(IMPORTANCE_DEFAULT));
                eq(IMPORTANCE_DEFAULT),
                eq(NotificationInfo.ACTION_NONE));
    }

    @Test
@@ -403,7 +411,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);

        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_NONE);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
@@ -420,7 +429,39 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
                eq(false) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(false) /*isNoisy */,
                eq(0));
                eq(0),
                eq(NotificationInfo.ACTION_NONE));
    }

    @Test
    public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
        ExpandableNotificationRow row = spy(mHelper.createRow());
        row.setBlockingHelperShowing(true);
        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
        when(row.getIsNonblockable()).thenReturn(false);
        StatusBarNotification statusBarNotification = row.getStatusBarNotification();

        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
                NotificationInfo.ACTION_BLOCK);

        verify(notificationInfoView).bindNotification(
                any(PackageManager.class),
                any(INotificationManager.class),
                eq(statusBarNotification.getPackageName()),
                any(NotificationChannel.class),
                anyInt(),
                eq(statusBarNotification),
                any(NotificationInfo.CheckSaveListener.class),
                any(NotificationInfo.OnSettingsClickListener.class),
                any(NotificationInfo.OnAppSettingsClickListener.class),
                eq(false),
                eq(false),
                eq(true) /* isForBlockingHelper */,
                eq(true) /* isUserSentimentNegative */,
                eq(false) /*isNoisy */,
                eq(0),
                eq(NotificationInfo.ACTION_BLOCK));
    }

    @Test
+131 −69

File changed.

Preview size limit exceeded, changes collapsed.