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

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

Merge "[Notif] Add Blocking helper to swipe" into pi-dev

parents c08aa83b 524cf7b6
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationListener;
@@ -130,6 +131,8 @@ public class SystemUIFactory {
        providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
        providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
        providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
        providers.put(NotificationBlockingHelperManager.class,
                () -> new NotificationBlockingHelperManager(context));
        providers.put(NotificationRemoteInputManager.class,
                () -> new NotificationRemoteInputManager(context));
        providers.put(SmartReplyConstants.class,
+42 −7
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar;

import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;

@@ -34,7 +35,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -127,6 +127,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
    private boolean mHasUserChangedExpansion;
    /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
    private boolean mUserExpanded;
    /** Whether the blocking helper is showing on this notification (even if dismissed) */
    private boolean mIsBlockingHelperShowing;

    /**
     * Has this notification been expanded while it was pinned
@@ -400,8 +402,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        updateIconVisibilities();
        updateShelfIconColor();

        showBlockingHelper(mEntry.userSentiment ==
                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
        showBlockingHelperButton(mEntry.userSentiment == USER_SENTIMENT_NEGATIVE);
        updateRippleAllowed();
    }

@@ -594,6 +595,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        return mNotificationParent != null;
    }

    /**
     * @return whether this notification is the only child in the group summary
     */
    public boolean isOnlyChildInGroup() {
        return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
    }

    public ExpandableNotificationRow getNotificationParent() {
        return mNotificationParent;
    }
@@ -1150,11 +1158,31 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        return mGroupParentWhenDismissed;
    }

    /**
     * Dismisses the notification with the option of showing the blocking helper in-place if we have
     * a negative user sentiment.
     *
     * @param fromAccessibility whether this dismiss is coming from an accessibility action
     * @return whether a blocking helper is shown in this row
     */
    public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
        NotificationBlockingHelperManager manager =
                Dependency.get(NotificationBlockingHelperManager.class);
        boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);

        // Continue with dismiss since we don't want the blocking helper to be directly associated
        // with a certain notification.
        performDismiss(fromAccessibility);
        return isBlockingHelperShown;
    }

    public void performDismiss(boolean fromAccessibility) {
        if (mGroupManager.isOnlyChildInGroup(getStatusBarNotification())) {
        if (isOnlyChildInGroup()) {
            ExpandableNotificationRow groupSummary =
                    mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
            if (groupSummary.isClearable()) {
                // If this is the only child in the group, dismiss the group, but don't try to show
                // the blocking helper affordance!
                groupSummary.performDismiss(fromAccessibility);
            }
        }
@@ -1166,6 +1194,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        }
    }

    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
        mIsBlockingHelperShowing = isBlockingHelperShowing;
    }

    public boolean isBlockingHelperShowing() {
        return mIsBlockingHelperShowing;
    }

    public void setOnDismissRunnable(Runnable onDismissRunnable) {
        mOnDismissRunnable = onDismissRunnable;
    }
@@ -1390,7 +1426,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        requestLayout();
    }

    public void showBlockingHelper(boolean show) {
    public void showBlockingHelperButton(boolean show) {
        mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
    }

@@ -1423,7 +1459,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
        mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};

        final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
        mHelperButton = findViewById(R.id.helper);
        mHelperButton.setOnClickListener(view -> {
            doLongClickCallback();
@@ -2526,7 +2561,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        }
        switch (action) {
            case AccessibilityNodeInfo.ACTION_DISMISS:
                performDismiss(true /* fromAccessibility */);
                performDismissWithBlockingHelper(true /* fromAccessibility */);
                return true;
            case AccessibilityNodeInfo.ACTION_COLLAPSE:
            case AccessibilityNodeInfo.ACTION_EXPAND:
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.statusbar;

import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;

import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;

/**
 * Manager for the notification blocking helper - tracks and helps create the blocking helper
 * affordance.
 */
public class NotificationBlockingHelperManager {
    /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
    private static final boolean DEBUG = false;
    private static final String TAG = "BlockingHelper";

    private final Context mContext;
    /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
    private ExpandableNotificationRow mBlockingHelperRow;

    /**
     * Whether the notification shade/stack is expanded - used to determine blocking helper
     * eligibility.
     */
    private boolean mIsShadeExpanded;

    public NotificationBlockingHelperManager(Context context) {
        mContext = context;
    }

    /**
     * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
     * item, in the current row if user sentiment is negative.
     *
     * @param row row to render the blocking helper in
     * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
     *                blocking helper UI
     * @return whether we're showing a blocking helper in the given notification row
     */
    boolean perhapsShowBlockingHelper(
            ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
        int numChildren = row.getNumberOfNotificationChildren();

        // We only show the blocking helper if:
        // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
        // - The notification shade is fully expanded (guarantees we're not touching a HUN).
        // - User sentiment is negative
        if (DEBUG
                || row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE
                && mIsShadeExpanded
                && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
            // Dismiss any current blocking helper before continuing forward (only one can be shown
            // at a given time).
            dismissCurrentBlockingHelper();

            if (DEBUG) {
                Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
            }
            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);

            // Enable blocking helper on the row before moving forward so everything in the guts is
            // correctly prepped.
            mBlockingHelperRow = row;
            mBlockingHelperRow.setBlockingHelperShowing(true);

            // We don't care about the touch origin (x, y) since we're opening guts without any
            // explicit user interaction.
            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
            return true;
        }
        return false;
    }

    /**
     * Dismiss the currently showing blocking helper, if any, through a notification update.
     *
     * @return whether the blocking helper was dismissed
     */
    boolean dismissCurrentBlockingHelper() {
        if (!isBlockingHelperRowNull()) {
            if (DEBUG) {
                Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
            }
            if (!mBlockingHelperRow.isBlockingHelperShowing()) {
                Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
                        + "Non-null row is not showing a blocking helper");
            }

            mBlockingHelperRow.setBlockingHelperShowing(false);
            if (mBlockingHelperRow.isAttachedToWindow()) {
                Dependency.get(NotificationEntryManager.class).updateNotifications();
            }
            mBlockingHelperRow = null;
            return true;
        }
        return false;
    }

    /**
     * Update the expansion status of the notification shade/stack.
     *
     * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
     */
    public void setNotificationShadeExpanded(float expandedHeight) {
        mIsShadeExpanded = expandedHeight > 0.0f;
    }

    @VisibleForTesting
    boolean isBlockingHelperRowNull() {
        return mBlockingHelperRow == null;
    }

    @VisibleForTesting
    void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
        mBlockingHelperRow = blockingHelperRowForTest;
    }
}
+107 −42
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;

import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -189,8 +190,12 @@ public class NotificationGuts extends FrameLayout {
    }

    public void openControls(
            int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
        animateOpen(x, y, onAnimationEnd);
            boolean shouldDoCircularReveal,
            int x,
            int y,
            boolean needsFalsingProtection,
            @Nullable Runnable onAnimationEnd) {
        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
        setExposed(true /* exposed */, needsFalsingProtection);
    }

@@ -204,7 +209,20 @@ public class NotificationGuts extends FrameLayout {
        }
    }

    /**
     * Closes any exposed guts/views.
     *
     * @param x x coordinate to animate the close circular reveal with
     * @param y y coordinate to animate the close circular reveal with
     * @param save whether the state should be saved
     * @param force whether the guts should be force-closed regardless of state.
     */
    public void closeControls(int x, int y, boolean save, boolean force) {
        // First try to dismiss any blocking helper.
        boolean wasBlockingHelperDismissed =
                Dependency.get(NotificationBlockingHelperManager.class)
                        .dismissCurrentBlockingHelper();

        if (getWindowToken() == null) {
            if (mClosedListener != null) {
                mClosedListener.onGutsClosed(this);
@@ -212,8 +230,12 @@ public class NotificationGuts extends FrameLayout {
            return;
        }

        if (mGutsContent == null || !mGutsContent.handleCloseControls(save, force)) {
            animateClose(x, y);
        if (mGutsContent == null
                || !mGutsContent.handleCloseControls(save, force)
                || wasBlockingHelperDismissed) {
            // We only want to do a circular reveal if we're not showing the blocking helper.
            animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);

            setExposed(false, mNeedsFalsingProtection);
            if (mClosedListener != null) {
                mClosedListener.onGutsClosed(this);
@@ -221,47 +243,58 @@ public class NotificationGuts extends FrameLayout {
        }
    }

    private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
        final double horz = Math.max(getWidth() - x, x);
        final double vert = Math.max(getHeight() - y, y);
        final float r = (float) Math.hypot(horz, vert);

        final Animator a
                = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
    /** Animates in the guts view via either a fade or a circular reveal. */
    private void animateOpen(
            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
        if (shouldDoCircularReveal) {
            double horz = Math.max(getWidth() - x, x);
            double vert = Math.max(getHeight() - y, y);
            float r = (float) Math.hypot(horz, vert);
            // Circular reveal originating at (x, y)
            Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
            a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
        a.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (onAnimationEnd != null) {
                    onAnimationEnd.run();
                }
            }
        });
            a.addListener(new AnimateOpenListener(onAnimationEnd));
            a.start();
        } else {
            // Fade in content
            this.setAlpha(0f);
            this.animate()
                    .alpha(1f)
                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
                    .setInterpolator(Interpolators.ALPHA_IN)
                    .setListener(new AnimateOpenListener(onAnimationEnd))
                    .start();
        }
    }

    private void animateClose(int x, int y) {

    /** Animates out the guts view via either a fade or a circular reveal. */
    private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
        if (shouldDoCircularReveal) {
            // Circular reveal originating at (x, y)
            if (x == -1 || y == -1) {
                x = (getLeft() + getRight()) / 2;
                y = (getTop() + getHeight() / 2);
            }
        final double horz = Math.max(getWidth() - x, x);
        final double vert = Math.max(getHeight() - y, y);
        final float r = (float) Math.hypot(horz, vert);
        final Animator a = ViewAnimationUtils.createCircularReveal(this,
            double horz = Math.max(getWidth() - x, x);
            double vert = Math.max(getHeight() - y, y);
            float r = (float) Math.hypot(horz, vert);
            Animator a = ViewAnimationUtils.createCircularReveal(this,
                    x, y, r, 0);
            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
        a.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                setVisibility(View.GONE);
            }
        });
            a.addListener(new AnimateCloseListener(this /* view */));
            a.start();
        } else {
            // Fade in the blocking helper.
            this.animate()
                    .alpha(0f)
                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
                    .setInterpolator(Interpolators.ALPHA_OUT)
                    .setListener(new AnimateCloseListener(this /* view */))
                    .start();
        }
    }

    public void setActualHeight(int actualHeight) {
@@ -336,4 +369,36 @@ public class NotificationGuts extends FrameLayout {
    public boolean isLeavebehind() {
        return mGutsContent != null && mGutsContent.isLeavebehind();
    }

    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
    private static class AnimateOpenListener extends AnimatorListenerAdapter {
        final Runnable mOnAnimationEnd;

        private AnimateOpenListener(Runnable onAnimationEnd) {
            mOnAnimationEnd = onAnimationEnd;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (mOnAnimationEnd != null) {
                mOnAnimationEnd.run();
            }
        }
    }

    /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
    private static class AnimateCloseListener extends AnimatorListenerAdapter {
        final View mView;

        private AnimateCloseListener(View view) {
            mView = view;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            mView.setVisibility(View.GONE);
        }
    }
}
+152 −104

File changed.

Preview size limit exceeded, changes collapsed.

Loading