Short shelf on lockscreen that animates width with transition to shade
NotificationIconContainer provides the shortest shelf width based on 3 icons + 1 overflow dot. Propagate fraction-to-shade from LockscreenShadeTransitionController => NSSLC => NSSL => NotificationShelf, which uses it to calculates shelf width NSSL requests children update, then NotificationShelf propagates shelf width => NotificationBackgroundView for corner roundness positioning => NotificationIconContainer to limit icons to new width NotificationShelf ensures that only the visible part of the shelf is tappable. Bug: 213480466 Test: NotificationShelfTest Test: swipe down on lockscreen notifications, let go => short shelf expands, retracts width Test: swipe down on lockscreen notifications to go to unlocked shade => short shelf expands to full width Test: expand notification on lockscreen => short shelf expands to full width as shade opens Test: tap hidden/visible part of shelf => only visible part of shelf launches full shade Test: the above in LTR/RTL Change-Id: I1d3f2a9077e6058d4459bb11602601c035b8043a
This commit is contained in:
@@ -337,11 +337,11 @@ class LockscreenShadeTransitionController @Inject constructor(
|
||||
if (field != value || forceApplyAmount) {
|
||||
field = value
|
||||
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
|
||||
nsslController.setTransitionToFullShadeAmount(field)
|
||||
qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
|
||||
nsslController.setTransitionToFullShadeAmount(field, qSDragProgress)
|
||||
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
|
||||
notificationPanelController.setTransitionToFullShadeAmount(field,
|
||||
false /* animate */, 0 /* delay */)
|
||||
qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
|
||||
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
|
||||
// TODO: appear media also in split shade
|
||||
val mediaAmount = if (useSplitShade) 0f else field
|
||||
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
@@ -32,6 +34,7 @@ import android.view.animation.PathInterpolator;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.SystemBarUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.animation.ShadeInterpolation;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
@@ -45,6 +48,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
|
||||
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
|
||||
import com.android.systemui.statusbar.notification.stack.ViewState;
|
||||
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
||||
import com.android.systemui.util.Utils;
|
||||
|
||||
/**
|
||||
* A notification shelf view that is placed inside the notification scroller. It manages the
|
||||
@@ -81,6 +85,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
private int mIndexOfFirstViewInShelf = -1;
|
||||
private float mCornerAnimationDistance;
|
||||
private NotificationShelfController mController;
|
||||
private int mActualWidth = -1;
|
||||
private boolean mUseSplitShade;
|
||||
|
||||
/** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
|
||||
private float mFractionToShade;
|
||||
|
||||
public NotificationShelf(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -122,13 +131,16 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
|
||||
setLayoutParams(layoutParams);
|
||||
|
||||
int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
|
||||
final int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
|
||||
mShelfIcons.setPadding(padding, 0, padding, 0);
|
||||
mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
|
||||
mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
|
||||
mCornerAnimationDistance = res.getDimensionPixelSize(
|
||||
R.dimen.notification_corner_animation_distance);
|
||||
|
||||
// TODO(b/213480466) enable short shelf on split shade
|
||||
mUseSplitShade = Utils.shouldUseSplitNotificationShade(mContext.getResources());
|
||||
|
||||
mShelfIcons.setInNotificationIconShelf(true);
|
||||
if (!mShowNotificationShelf) {
|
||||
setVisibility(GONE);
|
||||
@@ -203,6 +215,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
|
||||
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
|
||||
viewState.yTranslation = stackEnd - viewState.height;
|
||||
|
||||
final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN);
|
||||
final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade);
|
||||
updateStateWidth(viewState, fraction, shortestWidth);
|
||||
} else {
|
||||
viewState.hidden = true;
|
||||
viewState.location = ExpandableViewState.LOCATION_GONE;
|
||||
@@ -210,6 +226,77 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shelfState View state for NotificationShelf
|
||||
* @param fraction Fraction of lockscreen to shade transition
|
||||
* @param shortestWidth Shortest width to use for lockscreen shelf
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) {
|
||||
shelfState.actualWidth = !mUseSplitShade && mAmbientState.isOnKeyguard()
|
||||
? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction)
|
||||
: getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fractionToShade Fraction of lockscreen to shade transition
|
||||
*/
|
||||
public void setFractionToShade(float fractionToShade) {
|
||||
mFractionToShade = fractionToShade;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Actual width of shelf, accounting for possible ongoing width animation
|
||||
*/
|
||||
public int getActualWidth() {
|
||||
return mActualWidth > -1 ? mActualWidth : getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localX Click x from left of screen
|
||||
* @param slop Margin of error within which we count x for valid click
|
||||
* @param left Left of shelf, from left of screen
|
||||
* @param right Right of shelf, from left of screen
|
||||
* @return Whether click x was in view
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public boolean isXInView(float localX, float slop, float left, float right) {
|
||||
return (left - slop) <= localX && localX < (right + slop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localY Click y from top of shelf
|
||||
* @param slop Margin of error within which we count y for valid click
|
||||
* @param top Top of shelf
|
||||
* @param bottom Height of shelf
|
||||
* @return Whether click y was in view
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public boolean isYInView(float localY, float slop, float top, float bottom) {
|
||||
return (top - slop) <= localY && localY < (bottom + slop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localX Click x
|
||||
* @param localY Click y
|
||||
* @param slop Margin of error for valid click
|
||||
* @return Whether this click was on the visible (non-clipped) part of the shelf
|
||||
*/
|
||||
@Override
|
||||
public boolean pointInView(float localX, float localY, float slop) {
|
||||
final float containerWidth = getWidth();
|
||||
final float shelfWidth = getActualWidth();
|
||||
|
||||
final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
|
||||
final float right = isLayoutRtl() ? containerWidth : shelfWidth;
|
||||
|
||||
final float top = mClipTopAmount;
|
||||
final float bottom = getActualHeight();
|
||||
|
||||
return isXInView(localX, slop, left, right)
|
||||
&& isYInView(localY, slop, top, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the shelf appearance based on the other notifications around it. This transforms
|
||||
* the icons from the notification area into the shelf.
|
||||
@@ -732,11 +819,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
// we always want to clip to our sides, such that nothing can draw outside of these bounds
|
||||
int height = getResources().getDisplayMetrics().heightPixels;
|
||||
mClipRect.set(0, -height, getWidth(), height);
|
||||
mShelfIcons.setClipBounds(mClipRect);
|
||||
if (mShelfIcons != null) {
|
||||
mShelfIcons.setClipBounds(mClipRect);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRelativeOffset() {
|
||||
mCollapsedIcons.getLocationOnScreen(mTmp);
|
||||
if (mCollapsedIcons != null) {
|
||||
mCollapsedIcons.getLocationOnScreen(mTmp);
|
||||
}
|
||||
getLocationOnScreen(mTmp);
|
||||
}
|
||||
|
||||
@@ -831,9 +922,20 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
|
||||
}
|
||||
|
||||
private class ShelfState extends ExpandableViewState {
|
||||
public class ShelfState extends ExpandableViewState {
|
||||
private boolean hasItemsInStableShelf;
|
||||
private ExpandableView firstViewInShelf;
|
||||
public int actualWidth = -1;
|
||||
|
||||
private void updateShelfWidth(View view) {
|
||||
if (actualWidth < 0) {
|
||||
return;
|
||||
}
|
||||
mActualWidth = actualWidth;
|
||||
ActivatableNotificationView anv = (ActivatableNotificationView) view;
|
||||
anv.getBackgroundNormal().setActualWidth(actualWidth);
|
||||
mShelfIcons.setActualLayoutWidth(actualWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToView(View view) {
|
||||
@@ -846,19 +948,21 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
||||
updateAppearance();
|
||||
setHasItemsInStableShelf(hasItemsInStableShelf);
|
||||
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
||||
updateShelfWidth(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateTo(View child, AnimationProperties properties) {
|
||||
public void animateTo(View view, AnimationProperties properties) {
|
||||
if (!mShowNotificationShelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.animateTo(child, properties);
|
||||
super.animateTo(view, properties);
|
||||
setIndexOfFirstViewInShelf(firstViewInShelf);
|
||||
updateAppearance();
|
||||
setHasItemsInStableShelf(hasItemsInStableShelf);
|
||||
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
||||
updateShelfWidth(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
updateBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The background of this view.
|
||||
*/
|
||||
public NotificationBackgroundView getBackgroundNormal() {
|
||||
return mBackgroundNormal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.view.View;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||
|
||||
/**
|
||||
@@ -39,15 +40,17 @@ public class NotificationBackgroundView extends View {
|
||||
private final boolean mDontModifyCorners;
|
||||
private Drawable mBackground;
|
||||
private int mClipTopAmount;
|
||||
private int mActualHeight;
|
||||
private int mClipBottomAmount;
|
||||
private int mTintColor;
|
||||
private final float[] mCornerRadii = new float[8];
|
||||
private boolean mBottomIsRounded;
|
||||
private int mBackgroundTop;
|
||||
private boolean mBottomAmountClips = true;
|
||||
private int mActualHeight = -1;
|
||||
private int mActualWidth = -1;
|
||||
private boolean mExpandAnimationRunning;
|
||||
private float mActualWidth;
|
||||
private int mExpandAnimationWidth = -1;
|
||||
private int mExpandAnimationHeight = -1;
|
||||
private int mDrawableAlpha = 255;
|
||||
private boolean mIsPressedAllowed;
|
||||
|
||||
@@ -59,11 +62,12 @@ public class NotificationBackgroundView extends View {
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
|
||||
if (mClipTopAmount + mClipBottomAmount < getActualHeight() - mBackgroundTop
|
||||
|| mExpandAnimationRunning) {
|
||||
canvas.save();
|
||||
if (!mExpandAnimationRunning) {
|
||||
canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
|
||||
canvas.clipRect(0, mClipTopAmount, getWidth(),
|
||||
getActualHeight() - mClipBottomAmount);
|
||||
}
|
||||
draw(canvas, mBackground);
|
||||
canvas.restore();
|
||||
@@ -73,17 +77,23 @@ public class NotificationBackgroundView extends View {
|
||||
private void draw(Canvas canvas, Drawable drawable) {
|
||||
if (drawable != null) {
|
||||
int top = mBackgroundTop;
|
||||
int bottom = mActualHeight;
|
||||
int bottom = getActualHeight();
|
||||
if (mBottomIsRounded
|
||||
&& mBottomAmountClips
|
||||
&& !mExpandAnimationRunning) {
|
||||
bottom -= mClipBottomAmount;
|
||||
}
|
||||
int left = 0;
|
||||
int right = getWidth();
|
||||
final boolean isRtl = isLayoutRtl();
|
||||
final int width = getWidth();
|
||||
final int actualWidth = getActualWidth();
|
||||
|
||||
int left = isRtl ? width - actualWidth : 0;
|
||||
int right = isRtl ? width : actualWidth;
|
||||
|
||||
if (mExpandAnimationRunning) {
|
||||
left = (int) ((getWidth() - mActualWidth) / 2.0f);
|
||||
right = (int) (left + mActualWidth);
|
||||
// Horizontally center this background view inside of the container
|
||||
left = (int) ((width - actualWidth) / 2.0f);
|
||||
right = (int) (left + actualWidth);
|
||||
}
|
||||
drawable.setBounds(left, top, right, bottom);
|
||||
drawable.draw(canvas);
|
||||
@@ -152,8 +162,26 @@ public class NotificationBackgroundView extends View {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getActualHeight() {
|
||||
return mActualHeight;
|
||||
private int getActualHeight() {
|
||||
if (mExpandAnimationRunning && mExpandAnimationHeight > -1) {
|
||||
return mExpandAnimationHeight;
|
||||
} else if (mActualHeight > -1) {
|
||||
return mActualHeight;
|
||||
}
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
public void setActualWidth(int actualWidth) {
|
||||
mActualWidth = actualWidth;
|
||||
}
|
||||
|
||||
private int getActualWidth() {
|
||||
if (mExpandAnimationRunning && mExpandAnimationWidth > -1) {
|
||||
return mExpandAnimationWidth;
|
||||
} else if (mActualWidth > -1) {
|
||||
return mActualWidth;
|
||||
}
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
public void setClipTopAmount(int clipTopAmount) {
|
||||
@@ -241,9 +269,9 @@ public class NotificationBackgroundView extends View {
|
||||
}
|
||||
|
||||
/** Set the current expand animation size. */
|
||||
public void setExpandAnimationSize(int actualWidth, int actualHeight) {
|
||||
mActualHeight = actualHeight;
|
||||
mActualWidth = actualWidth;
|
||||
public void setExpandAnimationSize(int width, int height) {
|
||||
mExpandAnimationHeight = width;
|
||||
mExpandAnimationWidth = height;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -5442,6 +5442,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fraction Fraction of the lockscreen to shade transition. 0f for all other states.
|
||||
* Once the lockscreen to shade transition completes and the shade is 100% open
|
||||
* LockscreenShadeTransitionController resets fraction to 0
|
||||
* where it remains until the next lockscreen-to-shade transition.
|
||||
*/
|
||||
public void setFractionToShade(float fraction) {
|
||||
mShelf.setFractionToShade(fraction);
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener to when scrolling changes.
|
||||
*/
|
||||
|
||||
@@ -1511,10 +1511,18 @@ public class NotificationStackScrollLayoutController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of pixels we have currently dragged down if we're transitioning to the full
|
||||
* shade. 0.0f means we're not transitioning yet.
|
||||
* @param amount The amount of pixels we have currently dragged down
|
||||
* for the lockscreen to shade transition. 0f for all other states.
|
||||
* @param fraction The fraction of lockscreen to shade transition.
|
||||
* 0f for all other states.
|
||||
*
|
||||
* Once the lockscreen to shade transition completes and the shade is 100% open,
|
||||
* LockscreenShadeTransitionController resets amount and fraction to 0, where they remain
|
||||
* until the next lockscreen-to-shade transition.
|
||||
*/
|
||||
public void setTransitionToFullShadeAmount(float amount) {
|
||||
public void setTransitionToFullShadeAmount(float amount, float fraction) {
|
||||
mView.setFractionToShade(fraction);
|
||||
|
||||
float extraTopInset = 0.0f;
|
||||
if (mStatusBarStateController.getState() == KEYGUARD) {
|
||||
float overallProgress = MathUtils.saturate(amount / mView.getHeight());
|
||||
|
||||
@@ -135,7 +135,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
}
|
||||
}.setDuration(CONTENT_FADE_DURATION);
|
||||
|
||||
private static final int MAX_VISIBLE_ICONS_ON_LOCK = 5;
|
||||
private static final int MAX_ICONS_ON_AOD = 5;
|
||||
public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
|
||||
public static final int MAX_STATIC_ICONS = 4;
|
||||
private static final int MAX_DOTS = 1;
|
||||
|
||||
@@ -385,6 +386,19 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Width of shelf for the given number of icons and overflow dot
|
||||
*/
|
||||
public int calculateWidthFor(int numMaxIcons) {
|
||||
if (getChildCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (int) (getActualPaddingStart()
|
||||
+ numMaxIcons * mIconSize
|
||||
+ mOverflowWidth
|
||||
+ getActualPaddingEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the horizontal translations for each notification based on how much the icons
|
||||
* are inserted into the notification container.
|
||||
@@ -394,7 +408,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
float translationX = getActualPaddingStart();
|
||||
int firstOverflowIndex = -1;
|
||||
int childCount = getChildCount();
|
||||
int maxVisibleIcons = mOnLockScreen ? MAX_VISIBLE_ICONS_ON_LOCK :
|
||||
int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD :
|
||||
mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
|
||||
float layoutEnd = getLayoutEnd();
|
||||
float overflowStart = getMaxOverflowStart();
|
||||
@@ -414,7 +428,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
}
|
||||
boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
|
||||
&& iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
|
||||
boolean noOverflowAfter = i == childCount - 1;
|
||||
boolean isLastChild = i == childCount - 1;
|
||||
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
|
||||
? ((StatusBarIconView) view).getIconScaleIncreased()
|
||||
: 1f;
|
||||
@@ -423,10 +437,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
: StatusBarIconView.STATE_ICON;
|
||||
|
||||
boolean isOverflowing =
|
||||
(translationX > (noOverflowAfter ? layoutEnd - mIconSize
|
||||
(translationX > (isLastChild ? layoutEnd - mIconSize
|
||||
: overflowStart - mIconSize));
|
||||
if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
|
||||
firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
|
||||
firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
|
||||
mVisualOverflowStart = layoutEnd - mOverflowWidth;
|
||||
if (forceOverflow || mIsStaticLayout) {
|
||||
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
|
||||
|
||||
@@ -227,7 +227,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
|
||||
fun testDragDownAmountDoesntCallOutInLockedDownShade() {
|
||||
whenever(nsslController.isInLockedDownShade).thenReturn(true)
|
||||
transitionController.dragDownAmount = 10f
|
||||
verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
|
||||
verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
|
||||
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
|
||||
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
|
||||
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
|
||||
@@ -238,7 +238,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testDragDownAmountCallsOut() {
|
||||
transitionController.dragDownAmount = 10f
|
||||
verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
|
||||
verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
|
||||
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
|
||||
verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
|
||||
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.android.systemui.statusbar.notification.stack
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.NotificationShelf
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.*
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
/**
|
||||
* Tests for {@link NotificationShelf}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper
|
||||
class NotificationShelfTest : SysuiTestCase() {
|
||||
|
||||
private val shelf = NotificationShelf(context, /* attrs */ null)
|
||||
private val shelfState = shelf.viewState as NotificationShelf.ShelfState
|
||||
private val ambientState = mock(AmbientState::class.java)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
shelf.bind(ambientState, /* hostLayoutController */ null)
|
||||
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShadeWidth_BasedOnFractionToShade() {
|
||||
setFractionToShade(0f)
|
||||
setOnLockscreen(true)
|
||||
|
||||
shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
|
||||
assertTrue(shelfState.actualWidth == 10)
|
||||
|
||||
shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10)
|
||||
assertTrue(shelfState.actualWidth == 20)
|
||||
|
||||
shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10)
|
||||
assertTrue(shelfState.actualWidth == 30)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShelfIsLong_WhenNotOnLockscreen() {
|
||||
setFractionToShade(0f)
|
||||
setOnLockscreen(false)
|
||||
|
||||
shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
|
||||
assertTrue(shelfState.actualWidth == 30)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testX_inViewForClick() {
|
||||
val isXInView = shelf.isXInView(
|
||||
/* localX */ 5f,
|
||||
/* slop */ 5f,
|
||||
/* left */ 0f,
|
||||
/* right */ 10f)
|
||||
assertTrue(isXInView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testXSlop_inViewForClick() {
|
||||
val isLeftXSlopInView = shelf.isXInView(
|
||||
/* localX */ -3f,
|
||||
/* slop */ 5f,
|
||||
/* left */ 0f,
|
||||
/* right */ 10f)
|
||||
assertTrue(isLeftXSlopInView)
|
||||
|
||||
val isRightXSlopInView = shelf.isXInView(
|
||||
/* localX */ 13f,
|
||||
/* slop */ 5f,
|
||||
/* left */ 0f,
|
||||
/* right */ 10f)
|
||||
assertTrue(isRightXSlopInView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testX_notInViewForClick() {
|
||||
val isXLeftOfShelfInView = shelf.isXInView(
|
||||
/* localX */ -10f,
|
||||
/* slop */ 5f,
|
||||
/* left */ 0f,
|
||||
/* right */ 10f)
|
||||
assertFalse(isXLeftOfShelfInView)
|
||||
|
||||
val isXRightOfShelfInView = shelf.isXInView(
|
||||
/* localX */ 20f,
|
||||
/* slop */ 5f,
|
||||
/* left */ 0f,
|
||||
/* right */ 10f)
|
||||
assertFalse(isXRightOfShelfInView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testY_inViewForClick() {
|
||||
val isYInView = shelf.isYInView(
|
||||
/* localY */ 5f,
|
||||
/* slop */ 5f,
|
||||
/* top */ 0f,
|
||||
/* bottom */ 10f)
|
||||
assertTrue(isYInView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testYSlop_inViewForClick() {
|
||||
val isTopYSlopInView = shelf.isYInView(
|
||||
/* localY */ -3f,
|
||||
/* slop */ 5f,
|
||||
/* top */ 0f,
|
||||
/* bottom */ 10f)
|
||||
assertTrue(isTopYSlopInView)
|
||||
|
||||
val isBottomYSlopInView = shelf.isYInView(
|
||||
/* localY */ 13f,
|
||||
/* slop */ 5f,
|
||||
/* top */ 0f,
|
||||
/* bottom */ 10f)
|
||||
assertTrue(isBottomYSlopInView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testY_notInViewForClick() {
|
||||
val isYAboveShelfInView = shelf.isYInView(
|
||||
/* localY */ -10f,
|
||||
/* slop */ 5f,
|
||||
/* top */ 0f,
|
||||
/* bottom */ 5f)
|
||||
assertFalse(isYAboveShelfInView)
|
||||
|
||||
val isYBelowShelfInView = shelf.isYInView(
|
||||
/* localY */ 15f,
|
||||
/* slop */ 5f,
|
||||
/* top */ 0f,
|
||||
/* bottom */ 5f)
|
||||
assertFalse(isYBelowShelfInView)
|
||||
}
|
||||
|
||||
private fun setFractionToShade(fraction: Float) {
|
||||
shelf.setFractionToShade(fraction)
|
||||
}
|
||||
|
||||
private fun setOnLockscreen(isOnLockscreen: Boolean) {
|
||||
whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user