Merge "New shelf shape, notification corner animations on scroll" into sc-dev

This commit is contained in:
Lyn Han
2021-04-30 18:20:50 +00:00
committed by Android (Google) Code Review
6 changed files with 84 additions and 56 deletions

View File

@@ -181,6 +181,9 @@
<!-- Radius for notifications corners without adjacent notifications --> <!-- Radius for notifications corners without adjacent notifications -->
<dimen name="notification_corner_radius">28dp</dimen> <dimen name="notification_corner_radius">28dp</dimen>
<!-- Distance over which notification corner animations run, near the shelf while scrolling. -->
<dimen name="notification_corner_animation_distance">48dp</dimen>
<!-- Radius for notifications corners with adjacent notifications --> <!-- Radius for notifications corners with adjacent notifications -->
<dimen name="notification_corner_radius_small">4dp</dimen> <dimen name="notification_corner_radius_small">4dp</dimen>

View File

@@ -84,7 +84,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mCutoutHeight; private int mCutoutHeight;
private int mGapHeight; private int mGapHeight;
private int mIndexOfFirstViewInShelf = -1; private int mIndexOfFirstViewInShelf = -1;
private float mCornerAnimationDistance;
private NotificationShelfController mController; private NotificationShelfController mController;
public NotificationShelf(Context context, AttributeSet attrs) { public NotificationShelf(Context context, AttributeSet attrs) {
@@ -104,6 +104,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
setClipToPadding(false); setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false); mShelfIcons.setIsStaticLayout(false);
setBottomRoundness(1.0f, false /* animate */); setBottomRoundness(1.0f, false /* animate */);
setTopRoundness(1f, false /* animate */);
// Setting this to first in section to get the clipping to the top roundness correct. This // Setting this to first in section to get the clipping to the top roundness correct. This
// value determines the way we are clipping to the top roundness of the overall shade // value determines the way we are clipping to the top roundness of the overall shade
@@ -134,6 +135,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
mHiddenShelfIconSize = res.getDimensionPixelOffset(R.dimen.hidden_shelf_icon_size); mHiddenShelfIconSize = res.getDimensionPixelOffset(R.dimen.hidden_shelf_icon_size);
mGapHeight = res.getDimensionPixelSize(R.dimen.qs_notification_padding); mGapHeight = res.getDimensionPixelSize(R.dimen.qs_notification_padding);
mCornerAnimationDistance = res.getDimensionPixelSize(
R.dimen.notification_corner_animation_distance);
mShelfIcons.setInNotificationIconShelf(true); mShelfIcons.setInNotificationIconShelf(true);
if (!mShowNotificationShelf) { if (!mShowNotificationShelf) {
@@ -256,7 +259,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
boolean aboveShelf = ViewState.getFinalTranslationZ(child) > baseZHeight boolean aboveShelf = ViewState.getFinalTranslationZ(child) > baseZHeight
|| child.isPinned(); || child.isPinned();
boolean isLastChild = child == lastChild; boolean isLastChild = child == lastChild;
float rowTranslationY = child.getTranslationY(); final float viewStart = child.getTranslationY();
final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast, final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
expandingAnimated, isLastChild); expandingAnimated, isLastChild);
@@ -278,7 +281,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
ExpandableNotificationRow expandableRow = (ExpandableNotificationRow) child; ExpandableNotificationRow expandableRow = (ExpandableNotificationRow) child;
numViewsInShelf += inShelfAmount; numViewsInShelf += inShelfAmount;
int ownColorUntinted = expandableRow.getBackgroundColorWithoutTint(); int ownColorUntinted = expandableRow.getBackgroundColorWithoutTint();
if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) { if (viewStart >= shelfStart && mNotGoneIndex == -1) {
mNotGoneIndex = notGoneIndex; mNotGoneIndex = notGoneIndex;
setTintColor(previousColor); setTintColor(previousColor);
setOverrideTintColor(colorTwoBefore, transitionAmount); setOverrideTintColor(colorTwoBefore, transitionAmount);
@@ -317,26 +320,44 @@ public class NotificationShelf extends ActivatableNotificationView implements
notGoneIndex++; notGoneIndex++;
} }
final float viewEnd = viewStart + child.getActualHeight();
final float cornerAnimationDistance = mCornerAnimationDistance
* mAmbientState.getExpansionFraction();
final float cornerAnimationTop = shelfStart - cornerAnimationDistance;
if (child instanceof ActivatableNotificationView) { if (child instanceof ActivatableNotificationView) {
ActivatableNotificationView anv = ActivatableNotificationView anv =
(ActivatableNotificationView) child; (ActivatableNotificationView) child;
if (anv.isFirstInSection() && previousAnv != null
&& previousAnv.isLastInSection()) { if (viewStart < shelfStart
// If the top of the shelf is between the view before a gap and the view after a && !mHostLayoutController.isViewAffectedBySwipe(anv)
// gap then we need to adjust the shelf's top roundness. && !mAmbientState.isPulsing()
float distanceToGapBottom = child.getTranslationY() - getTranslationY(); && !mAmbientState.isDozing()) {
float distanceToGapTop = getTranslationY()
- (previousAnv.getTranslationY() + previousAnv.getActualHeight()); if (viewEnd >= cornerAnimationTop) {
if (distanceToGapTop > 0) { // Round bottom corners within animation bounds
// We interpolate our top roundness so that it's fully rounded if we're at final float changeFraction = MathUtils.saturate(
// the bottom of the gap, and not rounded at all if we're at the top of the (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
// gap (directly up against the bottom of previousAnv) final float roundness = anv.isLastInSection() ? 1f : changeFraction * 1f;
// Then we apply the same roundness to the bottom of previousAnv so that the anv.setBottomRoundness(roundness, false);
// corners join together as the shelf approaches previousAnv.
firstElementRoundness = (float) Math.min(1.0, } else if (viewEnd < cornerAnimationTop) {
distanceToGapTop / mGapHeight); // Fast scroll skips frames and leaves corners with unfinished rounding.
previousAnv.setBottomRoundness(firstElementRoundness, // Reset top and bottom corners outside of animation bounds.
false /* don't animate */); anv.setBottomRoundness(anv.isLastInSection() ? 1f : 0f, false);
}
if (viewStart >= cornerAnimationTop) {
// Round top corners within animation bounds
final float changeFraction = MathUtils.saturate(
(viewStart - cornerAnimationTop) / cornerAnimationDistance);
final float roundness = anv.isFirstInSection() ? 1f : changeFraction * 1f;
anv.setTopRoundness(roundness, false);
} else if (viewStart < cornerAnimationTop) {
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
anv.setTopRoundness(anv.isFirstInSection() ? 1f : 0f, false);
} }
} }
previousAnv = anv; previousAnv = anv;
@@ -394,7 +415,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
private void setFirstElementRoundness(float firstElementRoundness) { private void setFirstElementRoundness(float firstElementRoundness) {
if (mFirstElementRoundness != firstElementRoundness) { if (mFirstElementRoundness != firstElementRoundness) {
mFirstElementRoundness = firstElementRoundness; mFirstElementRoundness = firstElementRoundness;
setTopRoundness(firstElementRoundness, false /* animate */);
} }
} }

View File

@@ -84,8 +84,7 @@ public class NotificationBackgroundView extends View {
int bottom = mActualHeight; int bottom = mActualHeight;
if (mBottomIsRounded if (mBottomIsRounded
&& mBottomAmountClips && mBottomAmountClips
&& !mExpandAnimationRunning && !mExpandAnimationRunning) {
&& !mLastInSection) {
bottom -= mClipBottomAmount; bottom -= mClipBottomAmount;
} }
int left = 0; int left = 0;

View File

@@ -71,6 +71,13 @@ public class NotificationRoundnessManager {
} }
} }
public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
return expandableView != null
&& (expandableView == mSwipedView
|| expandableView == mViewBeforeSwipedView
|| expandableView == mViewAfterSwipedView);
}
boolean updateViewWithoutCallback(ExpandableView view, boolean updateViewWithoutCallback(ExpandableView view,
boolean animate) { boolean animate) {
if (view == null if (view == null
@@ -78,38 +85,35 @@ public class NotificationRoundnessManager {
|| view == mViewAfterSwipedView) { || view == mViewAfterSwipedView) {
return false; return false;
} }
float topRoundness = getRoundness(view, true /* top */);
float bottomRoundness = getRoundness(view, false /* top */); final float topRoundness = getRoundness(view, true /* top */);
boolean topChanged = view.setTopRoundness(topRoundness, animate); final float bottomRoundness = getRoundness(view, false /* top */);
boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);
boolean firstInSection = isFirstInSection(view, false /* exclude first section */); final boolean topChanged = view.setTopRoundness(topRoundness, animate);
boolean lastInSection = isLastInSection(view, false /* exclude last section */); final boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);
view.setFirstInSection(firstInSection);
view.setLastInSection(lastInSection); final boolean isFirstInSection = isFirstInSection(view);
return (firstInSection || lastInSection) && (topChanged || bottomChanged); final boolean isLastInSection = isLastInSection(view);
view.setFirstInSection(isFirstInSection);
view.setLastInSection(isLastInSection);
return (isFirstInSection || isLastInSection) && (topChanged || bottomChanged);
} }
private boolean isFirstInSection(ExpandableView view, boolean includeFirstSection) { private boolean isFirstInSection(ExpandableView view) {
int numNonEmptySections = 0;
for (int i = 0; i < mFirstInSectionViews.length; i++) { for (int i = 0; i < mFirstInSectionViews.length; i++) {
if (view == mFirstInSectionViews[i]) { if (view == mFirstInSectionViews[i]) {
return includeFirstSection || numNonEmptySections > 0; return true;
}
if (mFirstInSectionViews[i] != null) {
numNonEmptySections++;
} }
} }
return false; return false;
} }
private boolean isLastInSection(ExpandableView view, boolean includeLastSection) { private boolean isLastInSection(ExpandableView view) {
int numNonEmptySections = 0;
for (int i = mLastInSectionViews.length - 1; i >= 0; i--) { for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
if (view == mLastInSectionViews[i]) { if (view == mLastInSectionViews[i]) {
return includeLastSection || numNonEmptySections > 0; return true;
}
if (mLastInSectionViews[i] != null) {
numNonEmptySections++;
} }
} }
return false; return false;
@@ -172,10 +176,10 @@ public class NotificationRoundnessManager {
|| (view.isHeadsUpAnimatingAway()) && !mExpanded)) { || (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
return 1.0f; return 1.0f;
} }
if (isFirstInSection(view, true /* include first section */) && top) { if (isFirstInSection(view) && top) {
return 1.0f; return 1.0f;
} }
if (isLastInSection(view, true /* include last section */) && !top) { if (isLastInSection(view) && !top) {
return 1.0f; return 1.0f;
} }
if (view == mTrackedHeadsUp) { if (view == mTrackedHeadsUp) {
@@ -229,10 +233,8 @@ public class NotificationRoundnessManager {
: section.getLastVisibleChild()); : section.getLastVisibleChild());
if (newView == oldView) { if (newView == oldView) {
isStillPresent = true; isStillPresent = true;
if (oldView.isFirstInSection() != isFirstInSection(oldView, if (oldView.isFirstInSection() != isFirstInSection(oldView)
false /* exclude first section */) || oldView.isLastInSection() != isLastInSection(oldView)) {
|| oldView.isLastInSection() != isLastInSection(oldView,
false /* exclude last section */)) {
adjacentSectionChanged = true; adjacentSectionChanged = true;
} }
break; break;

View File

@@ -740,6 +740,10 @@ public class NotificationStackScrollLayoutController {
return true; return true;
} }
public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
return mNotificationRoundnessManager.isViewAffectedBySwipe(expandableView);
}
public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mView.addOnExpandedHeightChangedListener(listener); mView.addOnExpandedHeightChangedListener(listener);
} }

View File

@@ -315,8 +315,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Test @Test
public void testNoViewsFirstOrLastInSectionWhenSecondSectionEmpty() { public void testNoViewsFirstOrLastInSectionWhenSecondSectionEmpty() {
Assert.assertFalse(mFirst.isFirstInSection()); Assert.assertTrue(mFirst.isFirstInSection());
Assert.assertFalse(mFirst.isLastInSection()); Assert.assertTrue(mFirst.isLastInSection());
} }
@Test @Test
@@ -325,8 +325,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(null, null), createSection(null, null),
createSection(mSecond, mSecond) createSection(mSecond, mSecond)
}); });
Assert.assertFalse(mSecond.isFirstInSection()); Assert.assertTrue(mSecond.isFirstInSection());
Assert.assertFalse(mSecond.isLastInSection()); Assert.assertTrue(mSecond.isLastInSection());
} }
@Test @Test
@@ -335,10 +335,10 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(mFirst, mFirst), createSection(mFirst, mFirst),
createSection(mSecond, mSecond) createSection(mSecond, mSecond)
}); });
Assert.assertFalse(mFirst.isFirstInSection()); Assert.assertTrue(mFirst.isFirstInSection());
Assert.assertTrue(mFirst.isLastInSection()); Assert.assertTrue(mFirst.isLastInSection());
Assert.assertTrue(mSecond.isFirstInSection()); Assert.assertTrue(mSecond.isFirstInSection());
Assert.assertFalse(mSecond.isLastInSection()); Assert.assertTrue(mSecond.isLastInSection());
} }
private NotificationSection createSection(ExpandableNotificationRow first, private NotificationSection createSection(ExpandableNotificationRow first,