Merge "Fixed an issue where the qs panel wasn't properly placed" into oc-dr1-dev

am: ab67c300f3

Change-Id: I593cd3dd18eaf6a11f3eb961c0185150131772e6
This commit is contained in:
Selim Cinek
2017-06-07 23:09:37 +00:00
committed by android-build-merger
12 changed files with 301 additions and 21 deletions

View File

@@ -63,6 +63,8 @@ import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.NotificationInflater;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -162,6 +164,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mIsSystemChildExpanded;
private boolean mIsPinned;
private FalsingManager mFalsingManager;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
private boolean mJustClicked;
@@ -388,6 +391,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
expandedIcon.setStaticDrawableColor(color);
}
public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
mAboveShelfChangedListener = aboveShelfChangedListener;
}
@Override
public boolean isDimmable() {
if (!getShowingLayout().isDimmable()) {
@@ -442,6 +449,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setHeadsUp(boolean isHeadsUp) {
boolean wasAboveShelf = isAboveShelf();
int intrinsicBefore = getIntrinsicHeight();
mIsHeadsUp = isHeadsUp;
mPrivateLayout.setHeadsUp(isHeadsUp);
@@ -454,6 +462,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
if (isHeadsUp) {
setAboveShelf(true);
} else if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
@@ -634,6 +644,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void setPinned(boolean pinned) {
int intrinsicHeight = getIntrinsicHeight();
boolean wasAboveShelf = isAboveShelf();
mIsPinned = pinned;
if (intrinsicHeight != getIntrinsicHeight()) {
notifyHeightChanged(false /* needsAnimation */);
@@ -645,6 +656,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
setUserExpanded(true);
}
setChronometerRunning(mLastChronometerRunning);
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
public boolean isPinned() {
@@ -993,8 +1007,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
boolean wasAboveShelf = isAboveShelf();
mHeadsupDisappearRunning = headsUpAnimatingAway;
mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
/**
@@ -1555,6 +1573,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void setOnKeyguard(boolean onKeyguard) {
if (onKeyguard != mOnKeyguard) {
boolean wasAboveShelf = isAboveShelf();
final boolean wasExpanded = isExpanded();
mOnKeyguard = onKeyguard;
onExpansionChanged(false /* userAction */, wasExpanded);
@@ -1564,6 +1583,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
notifyHeightChanged(false /* needsAnimation */);
}
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
}
@@ -2214,7 +2236,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setAboveShelf(boolean aboveShelf) {
boolean wasAboveShelf = isAboveShelf();
mAboveShelf = aboveShelf;
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
public static class NotificationViewState extends ExpandableViewState {

View File

@@ -179,7 +179,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
}
mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
mShelfState.hidden = !mAmbientState.isShadeExpanded();
mShelfState.hidden = !mAmbientState.isShadeExpanded()
|| mAmbientState.isQsCustomizerShowing();
mShelfState.maxShelfEnd = maxShelfEnd;
} else {
mShelfState.hidden = true;

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2017 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.notification;
/**
* A listener for when the above shelf state of notification changes
*/
public interface AboveShelfChangedListener {
/**
* Notifies a listener that the above shelf state changed
*/
void onAboveShelfStateChanged(boolean aboveShelf);
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2017 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.notification;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.ExpandableNotificationRow;
/**
* An observer that listens to the above shelf state and can notify listeners
*/
public class AboveShelfObserver implements AboveShelfChangedListener {
private final ViewGroup mHostLayout;
private boolean mHasViewsAboveShelf = false;
private HasViewAboveShelfChangedListener mListener;
public AboveShelfObserver(ViewGroup hostLayout) {
mHostLayout = hostLayout;
}
public void setListener(HasViewAboveShelfChangedListener listener) {
mListener = listener;
}
@Override
public void onAboveShelfStateChanged(boolean aboveShelf) {
boolean hasViewsAboveShelf = aboveShelf;
if (!hasViewsAboveShelf && mHostLayout != null) {
int n = mHostLayout.getChildCount();
for (int i = 0; i < n; i++) {
View child = mHostLayout.getChildAt(i);
if (child instanceof ExpandableNotificationRow) {
if (((ExpandableNotificationRow) child).isAboveShelf()) {
hasViewsAboveShelf = true;
break;
}
}
}
}
if (mHasViewsAboveShelf != hasViewsAboveShelf) {
mHasViewsAboveShelf = hasViewsAboveShelf;
if (mListener != null) {
mListener.onHasViewsAboveShelfChanged(hasViewsAboveShelf);
}
}
}
@VisibleForTesting
boolean hasViewsAboveShelf() {
return mHasViewsAboveShelf;
}
public interface HasViewAboveShelfChangedListener {
void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf);
}
}

View File

@@ -34,18 +34,19 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
* The container with notification stack scroller and quick settings inside.
*/
public class NotificationsQuickSettingsContainer extends FrameLayout
implements OnInflateListener, FragmentListener, OnHeadsUpChangedListener {
implements OnInflateListener, FragmentListener,
AboveShelfObserver.HasViewAboveShelfChangedListener {
private FrameLayout mQsFrame;
private View mUserSwitcher;
private View mStackScroller;
private NotificationStackScrollLayout mStackScroller;
private View mKeyguardStatusBar;
private boolean mInflated;
private boolean mQsExpanded;
@@ -53,8 +54,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
private int mBottomPadding;
private int mStackScrollerMargin;
private boolean mHeadsUp;
private HeadsUpManager mHeadsUpManager;
private boolean mHasViewsAboveShelf;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -76,16 +76,12 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, this);
mHeadsUpManager = SysUiServiceProvider.getComponent(getContext(), StatusBar.class)
.mHeadsUpManager;
mHeadsUpManager.addListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
FragmentHostManager.get(this).removeTagListener(QS.TAG, this);
mHeadsUpManager.removeListener(this);
}
@Override
@@ -116,7 +112,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
final boolean qsBottom = mHeadsUp;
final boolean qsBottom = mHasViewsAboveShelf;
View stackQsTop = qsBottom ? mStackScroller : mQsFrame;
View stackQsBottom = !qsBottom ? mStackScroller : mQsFrame;
// Invert the order of the scroll view and user switcher such that the notifications receive
@@ -183,7 +179,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
setPadding(0, 0, 0, mBottomPadding);
setBottomMargin(mStackScroller, mStackScrollerMargin);
}
mStackScroller.setQsCustomizerShowing(isShowing);
}
private void setBottomMargin(View v, int bottomMargin) {
@@ -193,12 +189,8 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
}
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
boolean hasHeadsUp = mHeadsUpManager.getAllEntries().size() != 0;
if (mHeadsUp == hasHeadsUp) {
return;
}
mHeadsUp = hasHeadsUp;
public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
mHasViewsAboveShelf = hasViewsAboveShelf;
invalidate();
}
}

View File

@@ -209,6 +209,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.RowInflaterTask;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -1000,6 +1001,9 @@ public class StatusBar extends SystemUI implements DemoMode,
R.id.notification_stack_scroller);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
R.id.notification_container_parent));
mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
mNotificationIconAreaController = SystemUIFactory.getInstance()
@@ -5333,6 +5337,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// for heads up notifications
protected HeadsUpManager mHeadsUpManager;
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
@@ -6408,6 +6414,7 @@ public class StatusBar extends SystemUI implements DemoMode,
row.setExpansionLogger(this, entry.notification.getKey());
row.setGroupManager(mGroupManager);
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(mAboveShelfObserver);
row.setRemoteInputController(mRemoteInputController);
row.setOnExpandClickListener(this);
row.setRemoteViewClickHandler(mOnClickHandler);

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.v4.util.ArraySet;
@@ -65,7 +66,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private final ArrayMap<String, Long> mSnoozedPackages;
private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
private final int mDefaultSnoozeLengthMs;
private final Handler mHandler = new Handler();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
@@ -624,6 +625,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
@Override
public void onReorderingAllowed() {
mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
if (isHeadsUp(entry.key)) {
// Maybe the heads-up was removed already
@@ -631,6 +633,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
}
}
mEntriesToRemoveWhenReorderingAllowed.clear();
mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
}
public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {

View File

@@ -61,6 +61,7 @@ public class AmbientState {
private boolean mPanelFullWidth;
private boolean mHasPulsingNotifications;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
public AmbientState(Context context) {
reload(context);
@@ -314,4 +315,12 @@ public class AmbientState {
public boolean isUnlockHintRunning() {
return mUnlockHintRunning;
}
public boolean isQsCustomizerShowing() {
return mQsCustomizerShowing;
}
public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
mQsCustomizerShowing = qsCustomizerShowing;
}
}

View File

@@ -375,6 +375,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private boolean mHeadsUpAnimatingAway;
private int mStatusBarState;
private int mCachedBackgroundColor;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private Runnable mAnimateScroll = this::animateScroll;
public NotificationStackScrollLayout(Context context) {
@@ -4060,7 +4061,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
mNeedsAnimation = true;
if (!mIsExpanded && !isHeadsUp) {
@@ -4242,6 +4243,15 @@ public class NotificationStackScrollLayout extends ViewGroup
mAmbientState.setUnlockHintRunning(running);
}
public void setQsCustomizerShowing(boolean isShowing) {
mAmbientState.setQsCustomizerShowing(isShowing);
requestChildrenUpdate();
}
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
/**
* A listener that is notified when some child locations might have changed.
*/

View File

@@ -29,6 +29,7 @@ import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
import com.android.systemui.SysuiTestCase;
@@ -96,4 +97,40 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
row.setHideSensitive(true, false, 0, 0);
verify(row).updateShelfIconColor();
}
@Test
public void testAboveShelfChangedListenerCalled() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
row.setAboveShelfChangedListener(listener);
row.setHeadsUp(true);
verify(listener).onAboveShelfStateChanged(true);
}
@Test
public void testAboveShelfChangedListenerCalledPinned() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
row.setAboveShelfChangedListener(listener);
row.setPinned(true);
verify(listener).onAboveShelfStateChanged(true);
}
@Test
public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
row.setAboveShelfChangedListener(listener);
row.setHeadsUpAnimatingAway(true);
verify(listener).onAboveShelfStateChanged(true);
}
@Test
public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
row.setHeadsUp(true);
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
row.setAboveShelfChangedListener(listener);
row.setAboveShelf(false);
verify(listener).onAboveShelfStateChanged(false);
}
}

View File

@@ -24,12 +24,16 @@ import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
/**
* A helper class to create {@link ExpandableNotificationRow}
@@ -42,10 +46,12 @@ public class NotificationTestHelper {
private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
private ExpandableNotificationRow mRow;
private InflationException mException;
private HeadsUpManager mHeadsUpManager;
public NotificationTestHelper(Context context) {
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
}
public ExpandableNotificationRow createRow() throws Exception {
@@ -73,6 +79,8 @@ public class NotificationTestHelper {
});
ExpandableNotificationRow row = mRow;
row.setGroupManager(mGroupManager);
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(aboveShelf -> {});
UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
StatusBarNotification sbn = new StatusBarNotification("com.android.systemui",
"com.android.systemui", mId++, null, 1000,

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 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.notification;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AboveShelfObserverTest extends SysuiTestCase {
private AboveShelfObserver mObserver;
private FrameLayout mHostLayout;
private NotificationTestHelper mNotificationTestHelper;
private AboveShelfObserver.HasViewAboveShelfChangedListener mListener;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(getContext());
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
row.setAboveShelfChangedListener(mObserver);
mHostLayout.addView(row);
row = mNotificationTestHelper.createRow();
row.setAboveShelfChangedListener(mObserver);
mHostLayout.addView(row);
mListener = mock(AboveShelfObserver.HasViewAboveShelfChangedListener.class);
}
@Test
public void testObserverChangesWhenGoingAbove() {
ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0);
mObserver.setListener(mListener);
row.setHeadsUp(true);
verify(mListener).onHasViewsAboveShelfChanged(true);
}
@Test
public void testObserverChangesWhenGoingBelow() {
ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0);
row.setHeadsUp(true);
mObserver.setListener(mListener);
row.setHeadsUp(false);
verify(mListener).onHasViewsAboveShelfChanged(false);
}
@Test
public void testStaysAboveWhenOneGoesAway() {
ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0);
row.setHeadsUp(true);
row = (ExpandableNotificationRow) mHostLayout.getChildAt(1);
row.setHeadsUp(true);
row.setHeadsUp(false);
Assert.assertTrue("There are still views above the shelf but removing one cleared it",
mObserver.hasViewsAboveShelf());
}
}