Merge "New shelf shape, notification corner animations on scroll" into sc-dev
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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 */);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user