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

Commit 520ba643 authored by Josh Tsuji's avatar Josh Tsuji Committed by Android (Google) Code Review
Browse files

Merge "Align bubble behavior with DND settings." into qt-dev

parents 731490a1 dd4d9f9d
Loading
Loading
Loading
Loading
+54 −3
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.bubbles;

import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
@@ -51,6 +54,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.util.Log;
import android.view.Display;
import android.view.IPinnedStackController;
@@ -78,6 +82,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -143,6 +148,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi

    // Bubbles get added to the status bar view
    private final StatusBarWindowController mStatusBarWindowController;
    private final ZenModeController mZenModeController;
    private StatusBarStateListener mStatusBarStateListener;

    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
@@ -204,17 +210,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    @Inject
    public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
            BubbleData data, ConfigurationController configurationController,
            NotificationInterruptionStateProvider interruptionStateProvider) {
            NotificationInterruptionStateProvider interruptionStateProvider,
            ZenModeController zenModeController) {
        this(context, statusBarWindowController, data, null /* synchronizer */,
                configurationController, interruptionStateProvider);
                configurationController, interruptionStateProvider, zenModeController);
    }

    public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
            BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
            ConfigurationController configurationController,
            NotificationInterruptionStateProvider interruptionStateProvider) {
            NotificationInterruptionStateProvider interruptionStateProvider,
            ZenModeController zenModeController) {
        mContext = context;
        mNotificationInterruptionStateProvider = interruptionStateProvider;
        mZenModeController = zenModeController;
        mZenModeController.addCallback(new ZenModeController.Callback() {
            @Override
            public void onZenChanged(int zen) {
                updateStackViewForZenConfig();
            }

            @Override
            public void onConfigChanged(ZenModeConfig config) {
                updateStackViewForZenConfig();
            }
        });

        configurationController.addCallback(this /* configurationListener */);

@@ -260,6 +280,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            if (mExpandListener != null) {
                mStackView.setExpandListener(mExpandListener);
            }

            updateStackViewForZenConfig();
        }
    }

@@ -566,6 +588,35 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    };

    /**
     * Updates the stack view's suppression flags from the latest config from the zen (do not
     * disturb) controller.
     */
    private void updateStackViewForZenConfig() {
        final ZenModeConfig zenModeConfig = mZenModeController.getConfig();

        if (zenModeConfig == null || mStackView == null) {
            return;
        }

        final int suppressedEffects = zenModeConfig.suppressedVisualEffects;
        final boolean hideNotificationDotsSelected =
                (suppressedEffects & SUPPRESSED_EFFECT_BADGE) != 0;
        final boolean dontPopNotifsOnScreenSelected =
                (suppressedEffects & SUPPRESSED_EFFECT_PEEK) != 0;
        final boolean hideFromPullDownShadeSelected =
                (suppressedEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;

        final boolean dndEnabled = mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;

        mStackView.setSuppressNewDot(
                dndEnabled && hideNotificationDotsSelected);
        mStackView.setSuppressFlyout(
                dndEnabled && (dontPopNotifsOnScreenSelected
                        || hideFromPullDownShadeSelected));
    }

    /**
     * Lets any listeners know if bubble state has changed.
     * Updates the visibility of the bubbles based on current state.
     * Does not un-bubble, just hides or un-hides. Notifies any
     * {@link BubbleStateChangeListener}s of visibility changes.
+67 −7
Original line number Diff line number Diff line
@@ -287,6 +287,9 @@ public class BubbleStackView extends FrameLayout {
    private BubbleDismissView mDismissContainer;
    private Runnable mAfterMagnet;

    private boolean mSuppressNewDot = false;
    private boolean mSuppressFlyout = false;

    public BubbleStackView(Context context, BubbleData data,
                           @Nullable SurfaceSynchronizer synchronizer) {
        super(context);
@@ -690,6 +693,9 @@ public class BubbleStackView extends FrameLayout {
        mBubbleContainer.addView(bubble.iconView, 0,
                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
        ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters);
        if (bubble.iconView != null) {
            bubble.iconView.setSuppressDot(mSuppressNewDot, false /* animate */);
        }
        animateInFlyoutForBubble(bubble);
        requestUpdate();
        logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
@@ -1308,6 +1314,29 @@ public class BubbleStackView extends FrameLayout {
        }
    }

    /** Sets whether all bubbles in the stack should not show the 'new' dot. */
    void setSuppressNewDot(boolean suppressNewDot) {
        mSuppressNewDot = suppressNewDot;

        for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
            BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
            bv.setSuppressDot(suppressNewDot, true /* animate */);
        }
    }

    /**
     * Sets whether the flyout should not appear, even if the notif otherwise would generate one.
     */
    void setSuppressFlyout(boolean suppressFlyout) {
        mSuppressFlyout = suppressFlyout;
    }

    /**
     * Callback to run after the flyout hides. Also called if a new flyout is shown before the
     * previous one animates out.
     */
    private Runnable mAfterFlyoutHides;

    /**
     * Animates in the flyout for the given bubble, if available, and then hides it after some time.
     */
@@ -1319,22 +1348,48 @@ public class BubbleStackView extends FrameLayout {
        if (updateMessage != null
                && !isExpanded()
                && !mIsExpansionAnimating
                && !mIsGestureInProgress) {
                && !mIsGestureInProgress
                && !mSuppressFlyout) {
            if (bubble.iconView != null) {
                bubble.iconView.setSuppressDot(true /* suppressDot */, false /* animate */);
                // Temporarily suppress the dot while the flyout is visible.
                bubble.iconView.setSuppressDot(
                        true /* suppressDot */, false /* animate */);

                mFlyoutDragDeltaX = 0f;
                mFlyout.setAlpha(0f);

                if (mAfterFlyoutHides != null) {
                    mAfterFlyoutHides.run();
                }

                mAfterFlyoutHides = () -> {
                    if (bubble.iconView == null) {
                        return;
                    }

                    // If we're going to suppress the dot, make it visible first so it'll
                    // visibly animate away.
                    if (mSuppressNewDot) {
                        bubble.iconView.setSuppressDot(
                                false /* suppressDot */, false /* animate */);
                    }

                    // Reset dot suppression. If we're not suppressing due to DND, then
                    // stop suppressing it with no animation (since the flyout has
                    // transformed into the dot). If we are suppressing due to DND, animate
                    // it away.
                    bubble.iconView.setSuppressDot(
                            mSuppressNewDot /* suppressDot */,
                            mSuppressNewDot /* animate */);
                };

                // Post in case layout isn't complete and getWidth returns 0.
                post(() -> mFlyout.showFlyout(
                        updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
                        mStackAnimationController.isStackOnLeftSide(),
                        bubble.iconView.getBadgeColor(),
                        () -> {
                            bubble.iconView.setSuppressDot(
                                    false /* suppressDot */, false /* animate */);
                        }));
                        bubble.iconView.getBadgeColor(), mAfterFlyoutHides));
            }

            mFlyout.removeCallbacks(mHideFlyout);
            mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
            logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
@@ -1343,6 +1398,10 @@ public class BubbleStackView extends FrameLayout {

    /** Hide the flyout immediately and cancel any pending hide runnables. */
    private void hideFlyoutImmediate() {
        if (mAfterFlyoutHides != null) {
            mAfterFlyoutHides.run();
        }

        mFlyout.removeCallbacks(mHideFlyout);
        mFlyout.hideFlyout();
    }
@@ -1445,6 +1504,7 @@ public class BubbleStackView extends FrameLayout {
        int bubbsCount = mBubbleContainer.getChildCount();
        for (int i = 0; i < bubbsCount; i++) {
            BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
            bv.updateDotVisibility(true /* animate */);
            bv.setZ((BubbleController.MAX_BUBBLES
                    * getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i);

+19 −15
Original line number Diff line number Diff line
@@ -138,19 +138,6 @@ public class BubbleView extends FrameLayout {
        updateDotVisibility(animate, null /* after */);
    }

    /**
     * Changes the dot's visibility to match the bubble view's state, running the provided callback
     * after animation if requested.
     */
    void updateDotVisibility(boolean animate, Runnable after) {
        boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;

        if (animate) {
            animateDot(showDot, after);
        } else {
            mBadgedImageView.setShowDot(showDot);
        }
    }

    /**
     * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
@@ -177,18 +164,35 @@ public class BubbleView extends FrameLayout {
        return mBadgedImageView.getDotPosition();
    }

    /**
     * Changes the dot's visibility to match the bubble view's state, running the provided callback
     * after animation if requested.
     */
    private void updateDotVisibility(boolean animate, Runnable after) {
        boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;

        if (animate) {
            animateDot(showDot, after);
        } else {
            mBadgedImageView.setShowDot(showDot);
        }
    }

    /**
     * Animates the badge to show or hide.
     */
    private void animateDot(boolean showDot, Runnable after) {
        if (mBadgedImageView.isShowingDot() != showDot) {
            mBadgedImageView.setShowDot(showDot);
            if (showDot) {
                mBadgedImageView.setShowDot(true);
            }

            mBadgedImageView.clearAnimation();
            mBadgedImageView.animate().setDuration(200)
                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                    .setUpdateListener((valueAnimator) -> {
                        float fraction = valueAnimator.getAnimatedFraction();
                        fraction = showDot ? fraction : 1 - fraction;
                        fraction = showDot ? fraction : 1f - fraction;
                        mBadgedImageView.setDotScale(fraction);
                    }).withEndAction(() -> {
                        if (!showDot) {
+14 −3
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -69,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;

import org.junit.Before;
import org.junit.Test;
@@ -99,6 +101,10 @@ public class BubbleControllerTest extends SysuiTestCase {
    private DozeParameters mDozeParameters;
    @Mock
    private ConfigurationController mConfigurationController;
    @Mock
    private ZenModeController mZenModeController;
    @Mock
    private ZenModeConfig mZenModeConfig;

    private FrameLayout mStatusBarView;
    @Captor
@@ -162,6 +168,9 @@ public class BubbleControllerTest extends SysuiTestCase {
        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);

        mZenModeConfig.suppressedVisualEffects = 0;
        when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);

        TestableNotificationInterruptionStateProvider interruptionStateProvider =
                new TestableNotificationInterruptionStateProvider(mContext);
        interruptionStateProvider.setUpWithPresenter(
@@ -170,7 +179,8 @@ public class BubbleControllerTest extends SysuiTestCase {
                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
        mBubbleData = new BubbleData(mContext);
        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
                mBubbleData, mConfigurationController, interruptionStateProvider);
                mBubbleData, mConfigurationController, interruptionStateProvider,
                mZenModeController);
        mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
        mBubbleController.setExpandListener(mBubbleExpandListener);

@@ -628,9 +638,10 @@ public class BubbleControllerTest extends SysuiTestCase {
        TestableBubbleController(Context context,
                StatusBarWindowController statusBarWindowController, BubbleData data,
                ConfigurationController configurationController,
                NotificationInterruptionStateProvider interruptionStateProvider) {
                NotificationInterruptionStateProvider interruptionStateProvider,
                ZenModeController zenModeController) {
            super(context, statusBarWindowController, data, Runnable::run,
                    configurationController, interruptionStateProvider);
                    configurationController, interruptionStateProvider, zenModeController);
        }

        @Override