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
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 53862fb..ddb8166 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -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.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 @@
private Context mContext;
private BubbleDismissListener mDismissListener;
+ private BubbleStateChangeListener mStateChangeListener;
private Map<String, BubbleView> mBubbles = new HashMap<>();
private BubbleStackView mStackView;
@@ -66,6 +68,9 @@
// 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 @@
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);
@@ -97,6 +112,13 @@
}
/**
+ * 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 @@
* 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 @@
if (mDismissListener != null) {
mDismissListener.onStackDismissed();
}
+ updateBubblesShowing();
}
/**
@@ -150,25 +175,25 @@
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 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 @@
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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index d05893d..00d6b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -37,6 +37,7 @@
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;
@@ -56,8 +57,8 @@
* A implementation of HeadsUpManager for phone and car.
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
- ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
+ ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
+ OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
private static final String TAG = "HeadsUpManagerPhone";
private final View mStatusBarWindowView;
@@ -78,11 +79,13 @@
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 @@
}
});
Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
+ if (!hasBubbles) {
+ mBubbleGoingAway = true;
+ }
+ updateTouchableRegionListener();
+ });
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -210,6 +219,9 @@
mHeadsUpGoingAway = false;
updateTouchableRegionListener();
}
+ if (mBubbleController.hasBubbles() || !mIsExpanded) {
+ updateTouchableRegionListener();
+ }
}
}
@@ -310,6 +322,11 @@
} 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 @@
});
}
+ // 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) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index cf29cfa..62b6d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -65,7 +65,7 @@
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 @@
* @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 @@
onThemeChanged();
}
- public View getStatusBarView() {
+ public ViewGroup getStatusBarView() {
return mStatusBarView;
}
@@ -236,7 +236,7 @@
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 @@
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 @@
boolean backdropShowing;
boolean wallpaperSupportsAmbientMode;
boolean notTouchable;
+ boolean bubblesShowing;
/**
* The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index de26c70..98d0c6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -28,7 +28,7 @@
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.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 @@
@Mock
private DozeParameters mDozeParameters;
@Mock
- private View mStatusBarView;
+ private ViewGroup mStatusBarView;
@Mock
private IActivityManager mActivityManager;