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

Commit d1c78b26 authored by Mady Mellor's avatar Mady Mellor
Browse files

Make status bar full screen when bubbles are present

* Adds new state 'bubblesShowing' to StatusBarWindowController
* When bubbles are showing status bar will be full screen and
  the touchable region will be updated

Test: manual / existing tests pass (atest SystemUITests)
Bug: 111236845
Change-Id: I6d28d0313104929fba49d326a72209f701eb78d5
parent 5549dd2d
Loading
Loading
Loading
Loading
+67 −12
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package com.android.systemui.bubbles;

import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

@@ -26,6 +26,7 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.service.notification.StatusBarNotification;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -58,6 +59,7 @@ public class BubbleController {

    private Context mContext;
    private BubbleDismissListener mDismissListener;
    private BubbleStateChangeListener mStateChangeListener;

    private Map<String, BubbleView> mBubbles = new HashMap<>();
    private BubbleStackView mStackView;
@@ -66,6 +68,9 @@ public class BubbleController {
    // Bubbles get added to the status bar view
    private StatusBarWindowController mStatusBarWindowController;

    // Used for determining view rect for touch interaction
    private Rect mTempRect = new Rect();

    /**
     * Listener to find out about bubble / bubble stack dismissal events.
     */
@@ -81,6 +86,16 @@ public class BubbleController {
        void onBubbleDismissed(String key);
    }

    /**
     * Listener to be notified when some states of the bubbles change.
     */
    public interface BubbleStateChangeListener {
        /**
         * Called when the stack has bubbles or no longer has bubbles.
         */
        void onHasBubblesChanged(boolean hasBubbles);
    }

    public BubbleController(Context context) {
        mContext = context;
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -96,6 +111,13 @@ public class BubbleController {
        mDismissListener = listener;
    }

    /**
     * Set a listener to be notified when some states of the bubbles change.
     */
    public void setBubbleStateChangeListener(BubbleStateChangeListener listener) {
        mStateChangeListener = listener;
    }

    /**
     * Whether or not there are bubbles present, regardless of them being visible on the
     * screen (e.g. if on AOD).
@@ -124,7 +146,9 @@ public class BubbleController {
     * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
     */
    public void dismissStack() {
        mStackView.setVisibility(GONE);
        if (mStackView == null) {
            return;
        }
        Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
        // Reset the position of the stack (TODO - or should we save / respect last user position?)
        mStackView.setPosition(startPoint.x, startPoint.y);
@@ -134,6 +158,7 @@ public class BubbleController {
        if (mDismissListener != null) {
            mDismissListener.onStackDismissed();
        }
        updateBubblesShowing();
    }

    /**
@@ -150,25 +175,25 @@ public class BubbleController {
            bubble.setNotif(notif);
            mBubbles.put(bubble.getKey(), bubble);

            boolean setPosition = false;
            boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
            if (mStackView == null) {
                setPosition = true;
                mStackView = new BubbleStackView(mContext);
                ViewGroup sbv = (ViewGroup) mStatusBarWindowController.getStatusBarView();
                ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
                // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
                // between bubble and the shade
                int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1;
                sbv.addView(mStackView, bubblePosition,
                        new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
            }
            mStackView.setVisibility(VISIBLE);
            mStackView.addBubble(bubble);

            if (setPosition) {
                // Need to add the bubble to the stack before we can know the width
                Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
                mStackView.setPosition(startPoint.x, startPoint.y);
                mStackView.setVisibility(VISIBLE);
            }
            updateBubblesShowing();
        }
    }

@@ -177,13 +202,32 @@ public class BubbleController {
     */
    public void removeBubble(String key) {
        BubbleView bv = mBubbles.get(key);
        if (bv != null) {
        if (mStackView != null && bv != null) {
            mStackView.removeBubble(bv);
            bv.getEntry().setBubbleDismissed(true);
        }
        if (mDismissListener != null) {
            mDismissListener.onBubbleDismissed(key);
        }
        updateBubblesShowing();
    }

    private void updateBubblesShowing() {
        boolean hasBubblesShowing = false;
        for (BubbleView bv : mBubbles.values()) {
            if (!bv.getEntry().isBubbleDismissed()) {
                hasBubblesShowing = true;
                break;
            }
        }
        boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
        mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
        if (mStackView != null && !hasBubblesShowing) {
            mStackView.setVisibility(INVISIBLE);
        }
        if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
            mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
        }
    }

    /**
@@ -205,14 +249,25 @@ public class BubbleController {
        for (BubbleView view : viewsToRemove) {
            mBubbles.remove(view.getKey());
            mStackView.removeBubble(view);
            if (mBubbles.size() == 0) {
                ((ViewGroup) mStatusBarWindowController.getStatusBarView()).removeView(mStackView);
                mStackView = null;
            }
        }
        if (mStackView != null) {
            mStackView.setVisibility(visible ? VISIBLE : GONE);
            mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
            if (!visible) {
                collapseStack();
            }
        }
        updateBubblesShowing();
    }

    /**
     * Rect indicating the touchable region for the bubble stack / expanded stack.
     */
    public Rect getTouchableRegion() {
        if (mStackView == null || mStackView.getVisibility() != VISIBLE) {
            return null;
        }
        mStackView.getBoundsOnScreen(mTempRect);
        return mTempRect;
    }

    // TODO: factor in PIP location / maybe last place user had it
+22 −2
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
@@ -78,11 +79,13 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
    private int[] mTmpTwoArray = new int[2];
    private boolean mHeadsUpGoingAway;
    private boolean mWaitingOnCollapseWhenGoingAway;
    private boolean mBubbleGoingAway;
    private boolean mIsObserving;
    private int mStatusBarState;

    private final StateListener mStateListener = this::setStatusBarState;
    private AnimationStateHandler mAnimationStateHandler;
    private BubbleController mBubbleController = Dependency.get(BubbleController.class);

    private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
        private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
@@ -127,6 +130,12 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
            }
        });
        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
        mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
            if (!hasBubbles) {
                mBubbleGoingAway = true;
            }
            updateTouchableRegionListener();
        });
    }

    public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -210,6 +219,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
                mHeadsUpGoingAway = false;
                updateTouchableRegionListener();
            }
            if (mBubbleController.hasBubbles() || !mIsExpanded) {
                updateTouchableRegionListener();
            }
        }
    }

@@ -310,6 +322,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        } else {
            setCollapsedTouchableInsets(info);
        }
        Rect r = mBubbleController.getTouchableRegion();
        if (r != null) {
            info.touchableRegion.union(r);
        }
        mBubbleGoingAway = false;
    }

    private void setCollapsedTouchableInsets(ViewTreeObserver.InternalInsetsInfo info) {
@@ -428,8 +445,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        });
    }

    // TODO: some kind of TouchableRegionManager to deal with this, HeadsUpManager is not really
    // the right place
    private void updateTouchableRegionListener() {
        boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
                || mBubbleController.hasBubbles() || mBubbleGoingAway
                || mWaitingOnCollapseWhenGoingAway
                || mStatusBarWindowView.getRootWindowInsets().getDisplayCutout() != null;
        if (shouldObserve == mIsObserving) {
+20 −4
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
    private final WindowManager mWindowManager;
    private final IActivityManager mActivityManager;
    private final DozeParameters mDozeParameters;
    private View mStatusBarView;
    private ViewGroup mStatusBarView;
    private WindowManager.LayoutParams mLp;
    private WindowManager.LayoutParams mLpChanged;
    private boolean mHasTopUi;
@@ -109,7 +109,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
     * @param statusBarView The view to add.
     * @param barHeight The height of the status bar in collapsed state.
     */
    public void add(View statusBarView, int barHeight) {
    public void add(ViewGroup statusBarView, int barHeight) {

        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
@@ -138,7 +138,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
        onThemeChanged();
    }

    public View getStatusBarView() {
    public ViewGroup getStatusBarView() {
        return mStatusBarView;
    }

@@ -236,7 +236,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
    private boolean isExpanded(State state) {
        return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
                || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
                || state.headsUpShowing
                || state.headsUpShowing || state.bubblesShowing
                || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
    }

@@ -473,6 +473,21 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
        apply(mCurrentState);
    }

    /**
     * Sets whether there are bubbles showing on the screen.
     */
    public void setBubblesShowing(boolean bubblesShowing) {
        mCurrentState.bubblesShowing = bubblesShowing;
        apply(mCurrentState);
    }

    /**
     * The bubbles showing state for the status bar.
     */
    public boolean getBubblesShowing() {
        return mCurrentState.bubblesShowing;
    }

    public void setStateListener(OtherwisedCollapsedListener listener) {
        mListener = listener;
    }
@@ -525,6 +540,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
        boolean backdropShowing;
        boolean wallpaperSupportsAmbientMode;
        boolean notTouchable;
        boolean bubblesShowing;

        /**
         * The {@link StatusBar} state from the status bar.
+2 −3
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import android.app.IActivityManager;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.android.systemui.SysuiTestCase;
@@ -37,7 +37,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -51,7 +50,7 @@ public class StatusBarWindowControllerTest extends SysuiTestCase {
    @Mock
    private DozeParameters mDozeParameters;
    @Mock
    private View mStatusBarView;
    private ViewGroup mStatusBarView;
    @Mock
    private IActivityManager mActivityManager;