Merge "Expand notifications like accordian" into sc-dev
This commit is contained in:
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState;
|
|||||||
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
|
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
|
||||||
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
|
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
|
||||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
|
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
|
||||||
|
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
|
||||||
import com.android.systemui.statusbar.notification.stack.ViewState;
|
import com.android.systemui.statusbar.notification.stack.ViewState;
|
||||||
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
||||||
|
|
||||||
@@ -82,6 +83,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
private Rect mClipRect = new Rect();
|
private Rect mClipRect = new Rect();
|
||||||
private int mCutoutHeight;
|
private int mCutoutHeight;
|
||||||
private int mGapHeight;
|
private int mGapHeight;
|
||||||
|
private int mIndexOfFirstViewInShelf = -1;
|
||||||
|
private int mIndexOfFirstViewInOverflowingSection = -1;
|
||||||
|
|
||||||
private NotificationShelfController mController;
|
private NotificationShelfController mController;
|
||||||
|
|
||||||
public NotificationShelf(Context context, AttributeSet attrs) {
|
public NotificationShelf(Context context, AttributeSet attrs) {
|
||||||
@@ -159,30 +163,49 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Update the state of the shelf. */
|
/** Update the state of the shelf. */
|
||||||
public void updateState(AmbientState ambientState) {
|
public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
|
||||||
|
AmbientState ambientState) {
|
||||||
ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
|
ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
|
||||||
ShelfState viewState = (ShelfState) getViewState();
|
ShelfState viewState = (ShelfState) getViewState();
|
||||||
if (mShowNotificationShelf && lastView != null) {
|
if (mShowNotificationShelf && lastView != null) {
|
||||||
float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
|
|
||||||
+ ambientState.getStackTranslation();
|
|
||||||
ExpandableViewState lastViewState = lastView.getViewState();
|
ExpandableViewState lastViewState = lastView.getViewState();
|
||||||
float viewEnd = lastViewState.yTranslation + lastViewState.height;
|
|
||||||
viewState.copyFrom(lastViewState);
|
viewState.copyFrom(lastViewState);
|
||||||
|
|
||||||
viewState.height = getIntrinsicHeight();
|
viewState.height = getIntrinsicHeight();
|
||||||
viewState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
|
|
||||||
getFullyClosedTranslation());
|
|
||||||
viewState.zTranslation = ambientState.getBaseZHeight();
|
viewState.zTranslation = ambientState.getBaseZHeight();
|
||||||
viewState.clipTopAmount = 0;
|
viewState.clipTopAmount = 0;
|
||||||
viewState.alpha = 1f - ambientState.getHideAmount();
|
viewState.alpha = 1f - ambientState.getHideAmount();
|
||||||
viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
|
viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
|
||||||
viewState.hideSensitive = false;
|
viewState.hideSensitive = false;
|
||||||
viewState.xTranslation = getTranslationX();
|
viewState.xTranslation = getTranslationX();
|
||||||
|
viewState.hasItemsInStableShelf = lastViewState.inShelf;
|
||||||
|
viewState.firstViewInShelf = algorithmState.firstViewInShelf;
|
||||||
|
viewState.firstViewInOverflowSection = algorithmState.firstViewInOverflowSection;
|
||||||
if (mNotGoneIndex != -1) {
|
if (mNotGoneIndex != -1) {
|
||||||
viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
|
viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
|
||||||
}
|
}
|
||||||
viewState.hasItemsInStableShelf = lastViewState.inShelf;
|
|
||||||
viewState.hidden = !mAmbientState.isShadeExpanded()
|
viewState.hidden = !mAmbientState.isShadeExpanded()
|
||||||
|| mAmbientState.isQsCustomizerShowing();
|
|| mAmbientState.isQsCustomizerShowing()
|
||||||
|
|| algorithmState.firstViewInShelf == null;
|
||||||
|
|
||||||
|
final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
|
||||||
|
algorithmState.firstViewInShelf);
|
||||||
|
|
||||||
|
if (mAmbientState.isExpansionChanging()
|
||||||
|
&& algorithmState.firstViewInShelf != null
|
||||||
|
&& indexOfFirstViewInShelf > 0) {
|
||||||
|
|
||||||
|
// Show shelf if section before it is showing.
|
||||||
|
final ExpandableView viewBeforeShelf = algorithmState.visibleChildren.get(
|
||||||
|
indexOfFirstViewInShelf - 1);
|
||||||
|
if (viewBeforeShelf.getViewState().hidden) {
|
||||||
|
viewState.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
|
||||||
|
viewState.yTranslation = stackEnd - viewState.height;
|
||||||
} else {
|
} else {
|
||||||
viewState.hidden = true;
|
viewState.hidden = true;
|
||||||
viewState.location = ExpandableViewState.LOCATION_GONE;
|
viewState.location = ExpandableViewState.LOCATION_GONE;
|
||||||
@@ -199,13 +222,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
if (!mShowNotificationShelf) {
|
if (!mShowNotificationShelf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mShelfIcons.resetViewStates();
|
mShelfIcons.resetViewStates();
|
||||||
float shelfStart = getTranslationY();
|
float shelfStart = getTranslationY();
|
||||||
float numViewsInShelf = 0.0f;
|
float numViewsInShelf = 0.0f;
|
||||||
View lastChild = mAmbientState.getLastVisibleBackgroundChild();
|
View lastChild = mAmbientState.getLastVisibleBackgroundChild();
|
||||||
mNotGoneIndex = -1;
|
mNotGoneIndex = -1;
|
||||||
float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
|
|
||||||
// find the first view that doesn't overlap with the shelf
|
// find the first view that doesn't overlap with the shelf
|
||||||
int notGoneIndex = 0;
|
int notGoneIndex = 0;
|
||||||
int colorOfViewBeforeLast = NO_COLOR;
|
int colorOfViewBeforeLast = NO_COLOR;
|
||||||
@@ -233,22 +254,37 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
|
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float notificationClipEnd;
|
float notificationClipEnd;
|
||||||
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();
|
float rowTranslationY = child.getTranslationY();
|
||||||
|
|
||||||
|
final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
|
||||||
|
expandingAnimated, isLastChild);
|
||||||
|
|
||||||
|
final float stackEnd = mAmbientState.getStackY()
|
||||||
|
+ mAmbientState.getStackHeight();
|
||||||
|
// TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
|
||||||
if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
|
if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
|
||||||
notificationClipEnd = shelfStart + getIntrinsicHeight();
|
notificationClipEnd = stackEnd;
|
||||||
|
} else if (mAmbientState.isExpansionChanging()) {
|
||||||
|
if (mIndexOfFirstViewInOverflowingSection != -1
|
||||||
|
&& i >= mIndexOfFirstViewInOverflowingSection) {
|
||||||
|
// Clip notifications in (section overflowing into shelf) to shelf start.
|
||||||
|
notificationClipEnd = shelfStart - mPaddingBetweenElements;
|
||||||
|
} else {
|
||||||
|
// Clip notifications before the section overflowing into shelf
|
||||||
|
// to stackEnd because we do not show the shelf if the section right before the
|
||||||
|
// shelf is still hidden.
|
||||||
|
notificationClipEnd = stackEnd;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
notificationClipEnd = shelfStart - mPaddingBetweenElements;
|
notificationClipEnd = shelfStart - mPaddingBetweenElements;
|
||||||
}
|
}
|
||||||
int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
|
int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
|
||||||
clipTopAmount = Math.max(clipTop, clipTopAmount);
|
clipTopAmount = Math.max(clipTop, clipTopAmount);
|
||||||
|
|
||||||
final float inShelfAmount = updateShelfTransformation(child, scrollingFast,
|
|
||||||
expandingAnimated, isLastChild);
|
|
||||||
// If the current row is an ExpandableNotificationRow, update its color, roundedness,
|
// If the current row is an ExpandableNotificationRow, update its color, roundedness,
|
||||||
// and icon state.
|
// and icon state.
|
||||||
if (child instanceof ExpandableNotificationRow) {
|
if (child instanceof ExpandableNotificationRow) {
|
||||||
@@ -314,19 +350,23 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
distanceToGapTop / mGapHeight);
|
distanceToGapTop / mGapHeight);
|
||||||
previousAnv.setBottomRoundness(firstElementRoundness,
|
previousAnv.setBottomRoundness(firstElementRoundness,
|
||||||
false /* don't animate */);
|
false /* don't animate */);
|
||||||
backgroundTop = (int) distanceToGapBottom;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousAnv = anv;
|
previousAnv = anv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clipTransientViews();
|
clipTransientViews();
|
||||||
|
|
||||||
setClipTopAmount(clipTopAmount);
|
setClipTopAmount(clipTopAmount);
|
||||||
boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight();
|
|
||||||
if (mShowNotificationShelf) {
|
boolean isHidden = getViewState().hidden
|
||||||
|
|| clipTopAmount >= getIntrinsicHeight()
|
||||||
|
|| !mShowNotificationShelf
|
||||||
|
|| numViewsInShelf < 1f;
|
||||||
|
|
||||||
|
// TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
|
||||||
setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
|
setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
|
||||||
}
|
|
||||||
setBackgroundTop(backgroundTop);
|
setBackgroundTop(backgroundTop);
|
||||||
setFirstElementRoundness(firstElementRoundness);
|
setFirstElementRoundness(firstElementRoundness);
|
||||||
mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
|
mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
|
||||||
@@ -339,11 +379,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||||
updateIconClipAmount(row);
|
|
||||||
updateContinuousClipping(row);
|
updateContinuousClipping(row);
|
||||||
}
|
}
|
||||||
boolean hideBackground = numViewsInShelf < 1.0f;
|
boolean hideBackground = isHidden;
|
||||||
setHideBackground(hideBackground || backgroundForceHidden);
|
setHideBackground(hideBackground);
|
||||||
if (mNotGoneIndex == -1) {
|
if (mNotGoneIndex == -1) {
|
||||||
mNotGoneIndex = notGoneIndex;
|
mNotGoneIndex = notGoneIndex;
|
||||||
}
|
}
|
||||||
@@ -476,10 +515,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
/**
|
/**
|
||||||
* @return the amount how much this notification is in the shelf
|
* @return the amount how much this notification is in the shelf
|
||||||
*/
|
*/
|
||||||
private float updateShelfTransformation(ExpandableView view, boolean scrollingFast,
|
private float updateShelfTransformation(int i, ExpandableView view, boolean scrollingFast,
|
||||||
boolean expandingAnimated, boolean isLastChild) {
|
boolean expandingAnimated, boolean isLastChild) {
|
||||||
|
|
||||||
// Let calculate how much the view is in the shelf
|
// Let's calculate how much the view is in the shelf
|
||||||
float viewStart = view.getTranslationY();
|
float viewStart = view.getTranslationY();
|
||||||
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
|
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
|
||||||
float iconTransformStart = calculateIconTransformationStart(view);
|
float iconTransformStart = calculateIconTransformationStart(view);
|
||||||
@@ -496,12 +535,18 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
transformDistance,
|
transformDistance,
|
||||||
view.getMinHeight() - getIntrinsicHeight());
|
view.getMinHeight() - getIntrinsicHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
float viewEnd = viewStart + fullHeight;
|
float viewEnd = viewStart + fullHeight;
|
||||||
float fullTransitionAmount = 0.0f;
|
float fullTransitionAmount = 0.0f;
|
||||||
float iconTransitionAmount = 0.0f;
|
float iconTransitionAmount = 0.0f;
|
||||||
float shelfStart = getTranslationY();
|
float shelfStart = getTranslationY();
|
||||||
|
if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) {
|
||||||
if (viewEnd >= shelfStart
|
// TODO(b/172289889) handle icon placement for notification that is clipped by the shelf
|
||||||
|
if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) {
|
||||||
|
fullTransitionAmount = 1f;
|
||||||
|
iconTransitionAmount = 1f;
|
||||||
|
}
|
||||||
|
} else if (viewEnd >= shelfStart
|
||||||
&& (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
|
&& (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
|
||||||
&& (mAmbientState.isShadeExpanded()
|
&& (mAmbientState.isShadeExpanded()
|
||||||
|| (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
|
|| (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
|
||||||
@@ -572,7 +617,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
&& !mNoAnimationsInThisFrame;
|
&& !mNoAnimationsInThisFrame;
|
||||||
}
|
}
|
||||||
iconState.clampedAppearAmount = clampedAmount;
|
iconState.clampedAppearAmount = clampedAmount;
|
||||||
setIconTransformationAmount(view, transitionAmount, isLastChild);
|
setIconTransformationAmount(view, transitionAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTargetClipped(ExpandableView view) {
|
private boolean isTargetClipped(ExpandableView view) {
|
||||||
@@ -585,12 +630,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
+ view.getContentTranslation()
|
+ view.getContentTranslation()
|
||||||
+ view.getRelativeTopPadding(target)
|
+ view.getRelativeTopPadding(target)
|
||||||
+ target.getHeight();
|
+ target.getHeight();
|
||||||
|
|
||||||
return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
|
return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setIconTransformationAmount(ExpandableView view, float transitionAmount,
|
private void setIconTransformationAmount(ExpandableView view, float transitionAmount) {
|
||||||
boolean isLastChild) {
|
|
||||||
if (!(view instanceof ExpandableNotificationRow)) {
|
if (!(view instanceof ExpandableNotificationRow)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -601,7 +644,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
iconState.alpha = transitionAmount;
|
iconState.alpha = transitionAmount;
|
||||||
|
|
||||||
boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
|
boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
|
||||||
iconState.hidden = isAppearing
|
iconState.hidden = isAppearing
|
||||||
|| (view instanceof ExpandableNotificationRow
|
|| (view instanceof ExpandableNotificationRow
|
||||||
@@ -610,8 +652,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|
||||||
|| row.isAboveShelf()
|
|| row.isAboveShelf()
|
||||||
|| row.showingPulsing()
|
|| row.showingPulsing()
|
||||||
|| (!row.isInShelf() && isLastChild)
|
|
||||||
|| row.getTranslationZ() > mAmbientState.getBaseZHeight();
|
|| row.getTranslationZ() > mAmbientState.getBaseZHeight();
|
||||||
|
|
||||||
iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
|
iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
|
||||||
|
|
||||||
// Fade in icons at shelf start
|
// Fade in icons at shelf start
|
||||||
@@ -790,8 +832,19 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
mController = notificationShelfController;
|
mController = notificationShelfController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) {
|
||||||
|
mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstViewInOverflowingSection(ExpandableView firstViewInOverflowingSection) {
|
||||||
|
mIndexOfFirstViewInOverflowingSection =
|
||||||
|
mHostLayoutController.indexOfChild(firstViewInOverflowingSection);
|
||||||
|
}
|
||||||
|
|
||||||
private class ShelfState extends ExpandableViewState {
|
private class ShelfState extends ExpandableViewState {
|
||||||
private boolean hasItemsInStableShelf;
|
private boolean hasItemsInStableShelf;
|
||||||
|
private ExpandableView firstViewInShelf;
|
||||||
|
private ExpandableView firstViewInOverflowSection;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyToView(View view) {
|
public void applyToView(View view) {
|
||||||
@@ -800,6 +853,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
super.applyToView(view);
|
super.applyToView(view);
|
||||||
|
setIndexOfFirstViewInShelf(firstViewInShelf);
|
||||||
|
setFirstViewInOverflowingSection(firstViewInOverflowSection);
|
||||||
updateAppearance();
|
updateAppearance();
|
||||||
setHasItemsInStableShelf(hasItemsInStableShelf);
|
setHasItemsInStableShelf(hasItemsInStableShelf);
|
||||||
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
||||||
@@ -812,6 +867,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
super.animateTo(child, properties);
|
super.animateTo(child, properties);
|
||||||
|
setIndexOfFirstViewInShelf(firstViewInShelf);
|
||||||
|
setFirstViewInOverflowingSection(firstViewInOverflowSection);
|
||||||
updateAppearance();
|
updateAppearance();
|
||||||
setHasItemsInStableShelf(hasItemsInStableShelf);
|
setHasItemsInStableShelf(hasItemsInStableShelf);
|
||||||
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
|
|||||||
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
|
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
|
||||||
import com.android.systemui.statusbar.notification.stack.AmbientState;
|
import com.android.systemui.statusbar.notification.stack.AmbientState;
|
||||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
|
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
|
||||||
|
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
|
||||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||||
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
|
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
|
||||||
@@ -103,9 +104,10 @@ public class NotificationShelfController {
|
|||||||
return mView.getHeight();
|
return mView.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateState(AmbientState ambientState) {
|
public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
|
||||||
|
AmbientState ambientState) {
|
||||||
mAmbientState = ambientState;
|
mAmbientState = ambientState;
|
||||||
mView.updateState(ambientState);
|
mView.updateState(algorithmState, ambientState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIntrinsicHeight() {
|
public int getIntrinsicHeight() {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class AmbientState {
|
|||||||
private NotificationShelf mShelf;
|
private NotificationShelf mShelf;
|
||||||
private int mZDistanceBetweenElements;
|
private int mZDistanceBetweenElements;
|
||||||
private int mBaseZHeight;
|
private int mBaseZHeight;
|
||||||
private int mMaxLayoutHeight;
|
private int mContentHeight;
|
||||||
private ExpandableView mLastVisibleBackgroundChild;
|
private ExpandableView mLastVisibleBackgroundChild;
|
||||||
private float mCurrentScrollVelocity;
|
private float mCurrentScrollVelocity;
|
||||||
private int mStatusBarState;
|
private int mStatusBarState;
|
||||||
@@ -84,6 +84,75 @@ public class AmbientState {
|
|||||||
private boolean mIsShadeOpening;
|
private boolean mIsShadeOpening;
|
||||||
private float mSectionPadding;
|
private float mSectionPadding;
|
||||||
|
|
||||||
|
/** Distance of top of notifications panel from top of screen. */
|
||||||
|
private float mStackY = 0;
|
||||||
|
|
||||||
|
/** Height of notifications panel. */
|
||||||
|
private float mStackHeight = 0;
|
||||||
|
|
||||||
|
/** Fraction of shade expansion. */
|
||||||
|
private float mExpansionFraction;
|
||||||
|
|
||||||
|
/** Height of the notifications panel without top padding when expansion completes. */
|
||||||
|
private float mStackEndHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Height of the notifications panel without top padding when expansion completes.
|
||||||
|
*/
|
||||||
|
public float getStackEndHeight() {
|
||||||
|
return mStackEndHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stackEndHeight Height of the notifications panel without top padding
|
||||||
|
* when expansion completes.
|
||||||
|
*/
|
||||||
|
public void setStackEndHeight(float stackEndHeight) {
|
||||||
|
mStackEndHeight = stackEndHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stackY Distance of top of notifications panel from top of screen.
|
||||||
|
*/
|
||||||
|
public void setStackY(float stackY) {
|
||||||
|
mStackY = stackY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Distance of top of notifications panel from top of screen.
|
||||||
|
*/
|
||||||
|
public float getStackY() {
|
||||||
|
return mStackY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param expansionFraction Fraction of shade expansion.
|
||||||
|
*/
|
||||||
|
public void setExpansionFraction(float expansionFraction) {
|
||||||
|
mExpansionFraction = expansionFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Fraction of shade expansion.
|
||||||
|
*/
|
||||||
|
public float getExpansionFraction() {
|
||||||
|
return mExpansionFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stackHeight Height of notifications panel.
|
||||||
|
*/
|
||||||
|
public void setStackHeight(float stackHeight) {
|
||||||
|
mStackHeight = stackHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Height of notifications panel.
|
||||||
|
*/
|
||||||
|
public float getStackHeight() {
|
||||||
|
return mStackHeight;
|
||||||
|
}
|
||||||
|
|
||||||
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
|
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
|
||||||
private boolean mHasAlertEntries;
|
private boolean mHasAlertEntries;
|
||||||
|
|
||||||
@@ -263,8 +332,8 @@ public class AmbientState {
|
|||||||
if (mDozeAmount == 1.0f && !isPulseExpanding()) {
|
if (mDozeAmount == 1.0f && !isPulseExpanding()) {
|
||||||
return mShelf.getHeight();
|
return mShelf.getHeight();
|
||||||
}
|
}
|
||||||
int height = Math.max(mLayoutMinHeight,
|
int height = (int) Math.max(mLayoutMinHeight,
|
||||||
Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding);
|
Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
|
||||||
if (ignorePulseHeight) {
|
if (ignorePulseHeight) {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
@@ -313,8 +382,12 @@ public class AmbientState {
|
|||||||
return mShelf;
|
return mShelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLayoutMaxHeight(int maxLayoutHeight) {
|
public void setContentHeight(int contentHeight) {
|
||||||
mMaxLayoutHeight = maxLayoutHeight;
|
mContentHeight = contentHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getContentHeight() {
|
||||||
|
return mContentHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -658,6 +658,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
y = getHeight() - getEmptyBottomMargin();
|
y = getHeight() - getEmptyBottomMargin();
|
||||||
mDebugPaint.setColor(Color.GREEN);
|
mDebugPaint.setColor(Color.GREEN);
|
||||||
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
|
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
|
||||||
|
|
||||||
|
y = (int) (mAmbientState.getStackY());
|
||||||
|
mDebugPaint.setColor(Color.CYAN);
|
||||||
|
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
|
||||||
|
|
||||||
|
y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
|
||||||
|
mDebugPaint.setColor(Color.BLUE);
|
||||||
|
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,11 +1131,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
mTopPaddingNeedsAnimation = true;
|
mTopPaddingNeedsAnimation = true;
|
||||||
mNeedsAnimation = true;
|
mNeedsAnimation = true;
|
||||||
}
|
}
|
||||||
|
updateStackPosition();
|
||||||
requestChildrenUpdate();
|
requestChildrenUpdate();
|
||||||
notifyHeightChangeListener(null, animate);
|
notifyHeightChangeListener(null, animate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply expansion fraction to the y position and height of the notifications panel.
|
||||||
|
*/
|
||||||
|
private void updateStackPosition() {
|
||||||
|
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
|
||||||
|
mAmbientState.setStackY(
|
||||||
|
MathUtils.lerp(0, mTopPadding, mAmbientState.getExpansionFraction()));
|
||||||
|
final float shadeBottom = getHeight() - getEmptyBottomMargin();
|
||||||
|
mAmbientState.setStackEndHeight(shadeBottom - mTopPadding);
|
||||||
|
mAmbientState.setStackHeight(
|
||||||
|
MathUtils.lerp(0, shadeBottom - mTopPadding, mAmbientState.getExpansionFraction()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the height of the panel.
|
* Update the height of the panel.
|
||||||
*
|
*
|
||||||
@@ -1135,6 +1157,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
*/
|
*/
|
||||||
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
|
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
|
||||||
public void setExpandedHeight(float height) {
|
public void setExpandedHeight(float height) {
|
||||||
|
final float shadeBottom = getHeight() - getEmptyBottomMargin();
|
||||||
|
final float expansionFraction = MathUtils.constrain(height / shadeBottom, 0f, 1f);
|
||||||
|
mAmbientState.setExpansionFraction(expansionFraction);
|
||||||
|
updateStackPosition();
|
||||||
|
|
||||||
mExpandedHeight = height;
|
mExpandedHeight = height;
|
||||||
setIsExpanded(height > 0);
|
setIsExpanded(height > 0);
|
||||||
int minExpansionHeight = getMinExpansionHeight();
|
int minExpansionHeight = getMinExpansionHeight();
|
||||||
@@ -2067,7 +2094,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
|
mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
|
||||||
updateScrollability();
|
updateScrollability();
|
||||||
clampScrollPosition();
|
clampScrollPosition();
|
||||||
mAmbientState.setLayoutMaxHeight(mContentHeight);
|
updateStackPosition();
|
||||||
|
mAmbientState.setContentHeight(mContentHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -788,6 +788,10 @@ public class NotificationStackScrollLayoutController {
|
|||||||
return mView.getTranslationX();
|
return mView.getTranslationX();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int indexOfChild(View view) {
|
||||||
|
return mView.indexOfChild(view);
|
||||||
|
}
|
||||||
|
|
||||||
public void setOnHeightChangedListener(
|
public void setOnHeightChangedListener(
|
||||||
ExpandableView.OnHeightChangedListener listener) {
|
ExpandableView.OnHeightChangedListener listener) {
|
||||||
mView.setOnHeightChangedListener(listener);
|
mView.setOnHeightChangedListener(listener);
|
||||||
|
|||||||
@@ -20,21 +20,22 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.statusbar.EmptyShadeView;
|
|
||||||
import com.android.systemui.statusbar.NotificationShelf;
|
import com.android.systemui.statusbar.NotificationShelf;
|
||||||
|
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
|
||||||
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
|
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableView;
|
import com.android.systemui.statusbar.notification.row.ExpandableView;
|
||||||
import com.android.systemui.statusbar.notification.row.FooterView;
|
import com.android.systemui.statusbar.notification.row.FooterView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
|
* The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
|
||||||
@@ -92,20 +93,16 @@ public class StackScrollAlgorithm {
|
|||||||
|
|
||||||
// First we reset the view states to their default values.
|
// First we reset the view states to their default values.
|
||||||
resetChildViewStates();
|
resetChildViewStates();
|
||||||
|
|
||||||
initAlgorithmState(mHostView, algorithmState, ambientState);
|
initAlgorithmState(mHostView, algorithmState, ambientState);
|
||||||
|
|
||||||
updatePositionsForState(algorithmState, ambientState);
|
updatePositionsForState(algorithmState, ambientState);
|
||||||
|
|
||||||
updateZValuesForState(algorithmState, ambientState);
|
updateZValuesForState(algorithmState, ambientState);
|
||||||
|
|
||||||
updateHeadsUpStates(algorithmState, ambientState);
|
updateHeadsUpStates(algorithmState, ambientState);
|
||||||
updatePulsingStates(algorithmState, ambientState);
|
updatePulsingStates(algorithmState, ambientState);
|
||||||
|
|
||||||
updateDimmedActivatedHideSensitive(ambientState, algorithmState);
|
updateDimmedActivatedHideSensitive(ambientState, algorithmState);
|
||||||
updateClipping(algorithmState, ambientState);
|
updateClipping(algorithmState, ambientState);
|
||||||
updateSpeedBumpState(algorithmState, speedBumpIndex);
|
updateSpeedBumpState(algorithmState, speedBumpIndex);
|
||||||
updateShelfState(ambientState);
|
updateShelfState(algorithmState, ambientState);
|
||||||
getNotificationChildrenStates(algorithmState, ambientState);
|
getNotificationChildrenStates(algorithmState, ambientState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,10 +141,13 @@ public class StackScrollAlgorithm {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShelfState(AmbientState ambientState) {
|
private void updateShelfState(
|
||||||
|
StackScrollAlgorithmState algorithmState,
|
||||||
|
AmbientState ambientState) {
|
||||||
|
|
||||||
NotificationShelf shelf = ambientState.getShelf();
|
NotificationShelf shelf = ambientState.getShelf();
|
||||||
if (shelf != null) {
|
if (shelf != null) {
|
||||||
shelf.updateState(ambientState);
|
shelf.updateState(algorithmState, ambientState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +172,8 @@ public class StackScrollAlgorithm {
|
|||||||
&& ((ExpandableNotificationRow) child).isPinned();
|
&& ((ExpandableNotificationRow) child).isPinned();
|
||||||
if (mClipNotificationScrollToTop
|
if (mClipNotificationScrollToTop
|
||||||
&& (!state.inShelf || (isHeadsUp && !firstHeadsUp))
|
&& (!state.inShelf || (isHeadsUp && !firstHeadsUp))
|
||||||
&& newYTranslation < clipStart) {
|
&& newYTranslation < clipStart
|
||||||
|
&& !ambientState.isShadeOpening()) {
|
||||||
// The previous view is overlapping on top, clip!
|
// The previous view is overlapping on top, clip!
|
||||||
float overlapAmount = clipStart - newYTranslation;
|
float overlapAmount = clipStart - newYTranslation;
|
||||||
state.clipTopAmount = (int) overlapAmount;
|
state.clipTopAmount = (int) overlapAmount;
|
||||||
@@ -217,7 +218,6 @@ public class StackScrollAlgorithm {
|
|||||||
private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
|
private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
|
||||||
AmbientState ambientState) {
|
AmbientState ambientState) {
|
||||||
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
|
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
|
||||||
|
|
||||||
int scrollY = ambientState.getScrollY();
|
int scrollY = ambientState.getScrollY();
|
||||||
|
|
||||||
// Due to the overScroller, the stackscroller can have negative scroll state. This is
|
// Due to the overScroller, the stackscroller can have negative scroll state. This is
|
||||||
@@ -230,7 +230,6 @@ public class StackScrollAlgorithm {
|
|||||||
state.visibleChildren.clear();
|
state.visibleChildren.clear();
|
||||||
state.visibleChildren.ensureCapacity(childCount);
|
state.visibleChildren.ensureCapacity(childCount);
|
||||||
int notGoneIndex = 0;
|
int notGoneIndex = 0;
|
||||||
ExpandableView lastView = null;
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
|
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
|
||||||
if (v.getVisibility() != View.GONE) {
|
if (v.getVisibility() != View.GONE) {
|
||||||
@@ -255,12 +254,101 @@ public class StackScrollAlgorithm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
|
|
||||||
state.indexOfExpandingNotification = expandingNotification != null
|
state.firstViewInShelf = null;
|
||||||
? expandingNotification.isChildInGroup()
|
// Save y, sectionStart, sectionEnd from when shade is fully expanded.
|
||||||
? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
|
// Consider updating these states in updateContentView instead so that we don't have to
|
||||||
: state.visibleChildren.indexOf(expandingNotification)
|
// recalculate in every frame.
|
||||||
: -1;
|
float currentY = -scrollY;
|
||||||
|
int sectionStartIndex = 0;
|
||||||
|
int sectionEndIndex = 0;
|
||||||
|
for (int i = 0; i < state.visibleChildren.size(); i++) {
|
||||||
|
final ExpandableView view = state.visibleChildren.get(i);
|
||||||
|
// Add space between sections.
|
||||||
|
final boolean applyGapHeight = childNeedsGapHeight(
|
||||||
|
ambientState.getSectionProvider(), i,
|
||||||
|
view, getPreviousView(i, state));
|
||||||
|
if (applyGapHeight) {
|
||||||
|
currentY += mGapHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save index of first view in the shelf
|
||||||
|
final float shelfStart = ambientState.getStackEndHeight()
|
||||||
|
- ambientState.getShelf().getIntrinsicHeight();
|
||||||
|
if (currentY >= shelfStart
|
||||||
|
&& !(view instanceof FooterView)
|
||||||
|
&& state.firstViewInShelf == null) {
|
||||||
|
state.firstViewInShelf = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record y position when fully expanded
|
||||||
|
ExpansionData expansionData = new ExpansionData();
|
||||||
|
expansionData.fullyExpandedY = currentY;
|
||||||
|
state.expansionData.put(view, expansionData);
|
||||||
|
|
||||||
|
if (ambientState.getSectionProvider()
|
||||||
|
.beginsSection(view, getPreviousView(i, state))) {
|
||||||
|
|
||||||
|
// Save section start/end for views in the section before this new section
|
||||||
|
ExpandableView sectionStartView = state.visibleChildren.get(sectionStartIndex);
|
||||||
|
final float sectionStart =
|
||||||
|
state.expansionData.get(sectionStartView).fullyExpandedY;
|
||||||
|
|
||||||
|
ExpandableView sectionEndView = state.visibleChildren.get(sectionEndIndex);
|
||||||
|
float sectionEnd = state.expansionData.get(sectionEndView).fullyExpandedY
|
||||||
|
+ sectionEndView.getIntrinsicHeight();
|
||||||
|
|
||||||
|
// If we show the shelf, trim section end to shelf start
|
||||||
|
// This means section end > start for views in the shelf
|
||||||
|
if (state.firstViewInShelf != null && sectionEnd > shelfStart) {
|
||||||
|
sectionEnd = shelfStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update section bounds of every view in the previous section
|
||||||
|
// Consider using shared SectionInfo for views in same section to avoid looping back
|
||||||
|
for (int j = sectionStartIndex; j < i; j++) {
|
||||||
|
ExpandableView sectionView = state.visibleChildren.get(j);
|
||||||
|
ExpansionData viewExpansionData =
|
||||||
|
state.expansionData.get(sectionView);
|
||||||
|
viewExpansionData.sectionStart = sectionStart;
|
||||||
|
viewExpansionData.sectionEnd = sectionEnd;
|
||||||
|
state.expansionData.put(sectionView, viewExpansionData);
|
||||||
|
}
|
||||||
|
sectionStartIndex = i;
|
||||||
|
|
||||||
|
if (view instanceof FooterView) {
|
||||||
|
// Also record section bounds for FooterView (same as its own)
|
||||||
|
// because it is the last view and we won't get to this point again
|
||||||
|
// after the loop ends
|
||||||
|
ExpansionData footerExpansionData = state.expansionData.get(view);
|
||||||
|
footerExpansionData.sectionStart = expansionData.fullyExpandedY;
|
||||||
|
footerExpansionData.sectionEnd = expansionData.fullyExpandedY
|
||||||
|
+ view.getIntrinsicHeight();
|
||||||
|
state.expansionData.put(view, footerExpansionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sectionEndIndex = i;
|
||||||
|
currentY = currentY
|
||||||
|
+ getMaxAllowedChildHeight(view)
|
||||||
|
+ mPaddingBetweenElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Which view starts the section of the view right before the shelf?
|
||||||
|
// Save it for later when we clip views in that section to shelf start.
|
||||||
|
state.firstViewInOverflowSection = null;
|
||||||
|
if (state.firstViewInShelf != null) {
|
||||||
|
ExpandableView nextView = null;
|
||||||
|
final int startIndex = state.visibleChildren.indexOf(state.firstViewInShelf);
|
||||||
|
for (int i = startIndex - 1; i >= 0; i--) {
|
||||||
|
ExpandableView view = state.visibleChildren.get(i);
|
||||||
|
if (nextView != null && ambientState.getSectionProvider()
|
||||||
|
.beginsSection(nextView, view)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextView = view;
|
||||||
|
}
|
||||||
|
state.firstViewInOverflowSection = nextView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
|
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
|
||||||
@@ -272,6 +360,10 @@ public class StackScrollAlgorithm {
|
|||||||
return notGoneIndex;
|
return notGoneIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ExpandableView getPreviousView(int i, StackScrollAlgorithmState algorithmState) {
|
||||||
|
return i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the positions for the views. This is the main part of the algorithm.
|
* Determine the positions for the views. This is the main part of the algorithm.
|
||||||
*
|
*
|
||||||
@@ -288,6 +380,15 @@ public class StackScrollAlgorithm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setLocation(ExpandableViewState expandableViewState, float currentYPosition,
|
||||||
|
int i) {
|
||||||
|
expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
|
||||||
|
if (currentYPosition <= 0) {
|
||||||
|
expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/172289889) polish shade open from HUN
|
||||||
/**
|
/**
|
||||||
* Populates the {@link ExpandableViewState} for a single child.
|
* Populates the {@link ExpandableViewState} for a single child.
|
||||||
*
|
*
|
||||||
@@ -306,53 +407,84 @@ public class StackScrollAlgorithm {
|
|||||||
StackScrollAlgorithmState algorithmState,
|
StackScrollAlgorithmState algorithmState,
|
||||||
AmbientState ambientState,
|
AmbientState ambientState,
|
||||||
float currentYPosition) {
|
float currentYPosition) {
|
||||||
ExpandableView child = algorithmState.visibleChildren.get(i);
|
|
||||||
ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
|
ExpandableView view = algorithmState.visibleChildren.get(i);
|
||||||
|
ExpandableViewState viewState = view.getViewState();
|
||||||
|
viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
|
||||||
|
viewState.alpha = 1f - ambientState.getHideAmount();
|
||||||
|
|
||||||
|
if (view.mustStayOnScreen() && viewState.yTranslation >= 0) {
|
||||||
|
// Even if we're not scrolled away we're in view and we're also not in the
|
||||||
|
// shelf. We can relax the constraints and let us scroll off the top!
|
||||||
|
float end = viewState.yTranslation + viewState.height + ambientState.getStackY();
|
||||||
|
viewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/172289889) move sectionFraction and showSection to initAlgorithmState
|
||||||
|
// Get fraction of section showing, and later apply it to view height and gaps between views
|
||||||
|
float sectionFraction = 1f;
|
||||||
|
boolean showSection = true;
|
||||||
|
|
||||||
|
if (!ambientState.isOnKeyguard()
|
||||||
|
&& !ambientState.isPulseExpanding()
|
||||||
|
&& ambientState.isExpansionChanging()) {
|
||||||
|
|
||||||
|
final ExpansionData expansionData = algorithmState.expansionData.get(view);
|
||||||
|
final float sectionHeight = expansionData.sectionEnd - expansionData.sectionStart;
|
||||||
|
sectionFraction = MathUtils.constrain(
|
||||||
|
(ambientState.getStackHeight() - expansionData.sectionStart) / sectionHeight,
|
||||||
|
0f, 1f);
|
||||||
|
showSection = expansionData.sectionStart < ambientState.getStackHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add gap between sections.
|
||||||
final boolean applyGapHeight =
|
final boolean applyGapHeight =
|
||||||
childNeedsGapHeight(
|
childNeedsGapHeight(
|
||||||
ambientState.getSectionProvider(), i,
|
ambientState.getSectionProvider(), i,
|
||||||
child, previousChild);
|
view, getPreviousView(i, algorithmState));
|
||||||
ExpandableViewState childViewState = child.getViewState();
|
|
||||||
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
|
|
||||||
|
|
||||||
if (applyGapHeight) {
|
if (applyGapHeight) {
|
||||||
currentYPosition += mGapHeight;
|
currentYPosition += sectionFraction * mGapHeight;
|
||||||
}
|
|
||||||
int childHeight = getMaxAllowedChildHeight(child);
|
|
||||||
childViewState.yTranslation = currentYPosition;
|
|
||||||
childViewState.alpha = 1f - ambientState.getHideAmount();
|
|
||||||
|
|
||||||
boolean isFooterView = child instanceof FooterView;
|
|
||||||
boolean isEmptyShadeView = child instanceof EmptyShadeView;
|
|
||||||
|
|
||||||
childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
|
|
||||||
float inset = ambientState.getTopPadding() + ambientState.getStackTranslation()
|
|
||||||
+ ambientState.getSectionPadding();
|
|
||||||
if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
|
|
||||||
// Even if we're not scrolled away we're in view and we're also not in the
|
|
||||||
// shelf. We can relax the constraints and let us scroll off the top!
|
|
||||||
float end = childViewState.yTranslation + childViewState.height + inset;
|
|
||||||
childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
|
|
||||||
}
|
|
||||||
if (isFooterView) {
|
|
||||||
childViewState.yTranslation = Math.min(childViewState.yTranslation,
|
|
||||||
ambientState.getInnerHeight() - childHeight);
|
|
||||||
} else if (isEmptyShadeView) {
|
|
||||||
childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
|
|
||||||
+ ambientState.getStackTranslation() * 0.25f;
|
|
||||||
} else if (child != ambientState.getTrackedHeadsUpRow()) {
|
|
||||||
clampPositionToShelf(child, childViewState, ambientState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
|
viewState.yTranslation = currentYPosition;
|
||||||
if (currentYPosition <= 0) {
|
|
||||||
childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
|
if (view instanceof SectionHeaderView) {
|
||||||
}
|
// Add padding before sections for overscroll effect.
|
||||||
if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
|
viewState.yTranslation += ambientState.getSectionPadding();
|
||||||
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
childViewState.yTranslation += inset;
|
if (view != ambientState.getTrackedHeadsUpRow()) {
|
||||||
|
if (ambientState.isExpansionChanging()) {
|
||||||
|
viewState.hidden = !showSection;
|
||||||
|
viewState.inShelf = algorithmState.firstViewInShelf != null
|
||||||
|
&& i >= algorithmState.visibleChildren.indexOf(
|
||||||
|
algorithmState.firstViewInShelf)
|
||||||
|
&& !(view instanceof FooterView);
|
||||||
|
} else {
|
||||||
|
// When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
|
||||||
|
// to shelf start, thereby hiding all notifications (except the first one, which we
|
||||||
|
// later unhide in updatePulsingState)
|
||||||
|
final int shelfStart = ambientState.getInnerHeight()
|
||||||
|
- ambientState.getShelf().getIntrinsicHeight();
|
||||||
|
if (!(view instanceof FooterView)) {
|
||||||
|
viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart);
|
||||||
|
}
|
||||||
|
if (viewState.yTranslation >= shelfStart) {
|
||||||
|
viewState.hidden = !view.isExpandAnimationRunning()
|
||||||
|
&& !view.hasExpandingChild()
|
||||||
|
&& !(view instanceof FooterView);
|
||||||
|
viewState.inShelf = true;
|
||||||
|
// Notifications in the shelf cannot be visible HUNs.
|
||||||
|
viewState.headsUpIsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewState.height = (int) MathUtils.lerp(
|
||||||
|
0, getMaxAllowedChildHeight(view), sectionFraction);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentYPosition += viewState.height + sectionFraction * mPaddingBetweenElements;
|
||||||
|
setLocation(view.getViewState(), currentYPosition, i);
|
||||||
|
viewState.yTranslation += ambientState.getStackY();
|
||||||
return currentYPosition;
|
return currentYPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,10 +525,10 @@ public class StackScrollAlgorithm {
|
|||||||
int visibleIndex,
|
int visibleIndex,
|
||||||
View child,
|
View child,
|
||||||
View previousChild) {
|
View previousChild) {
|
||||||
|
return sectionProvider.beginsSection(child, previousChild)
|
||||||
boolean needsGapHeight = sectionProvider.beginsSection(child, previousChild)
|
&& visibleIndex > 0
|
||||||
&& visibleIndex > 0;
|
&& !(previousChild instanceof SilentHeader)
|
||||||
return needsGapHeight;
|
&& !(child instanceof FooterView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
|
private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
|
||||||
@@ -514,42 +646,6 @@ public class StackScrollAlgorithm {
|
|||||||
childState.yTranslation = newTranslation;
|
childState.yTranslation = newTranslation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clamp the height of the child down such that its end is at most on the beginning of
|
|
||||||
* the shelf.
|
|
||||||
*
|
|
||||||
* @param childViewState the view state of the child
|
|
||||||
* @param ambientState the ambient state
|
|
||||||
*/
|
|
||||||
private void clampPositionToShelf(ExpandableView child,
|
|
||||||
ExpandableViewState childViewState,
|
|
||||||
AmbientState ambientState) {
|
|
||||||
if (ambientState.getShelf() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpandableNotificationRow trackedHeadsUpRow = ambientState.getTrackedHeadsUpRow();
|
|
||||||
boolean isBeforeTrackedHeadsUp = trackedHeadsUpRow != null
|
|
||||||
&& mHostView.indexOfChild(child) < mHostView.indexOfChild(trackedHeadsUpRow);
|
|
||||||
|
|
||||||
int shelfStart = ambientState.getInnerHeight()
|
|
||||||
- ambientState.getShelf().getIntrinsicHeight();
|
|
||||||
if (ambientState.isAppearing() && !child.isAboveShelf() && !isBeforeTrackedHeadsUp) {
|
|
||||||
// Don't show none heads-up notifications while in appearing phase.
|
|
||||||
childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
|
|
||||||
}
|
|
||||||
childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
|
|
||||||
if (child instanceof SectionHeaderView) {
|
|
||||||
// Add padding before sections for overscroll effect.
|
|
||||||
childViewState.yTranslation += ambientState.getSectionPadding();
|
|
||||||
}
|
|
||||||
if (childViewState.yTranslation >= shelfStart) {
|
|
||||||
childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
|
|
||||||
childViewState.inShelf = true;
|
|
||||||
childViewState.headsUpIsVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getMaxAllowedChildHeight(View child) {
|
protected int getMaxAllowedChildHeight(View child) {
|
||||||
if (child instanceof ExpandableView) {
|
if (child instanceof ExpandableView) {
|
||||||
ExpandableView expandableView = (ExpandableView) child;
|
ExpandableView expandableView = (ExpandableView) child;
|
||||||
@@ -641,6 +737,35 @@ public class StackScrollAlgorithm {
|
|||||||
this.mIsExpanded = isExpanded;
|
this.mIsExpanded = isExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data used to layout views while shade expansion changes.
|
||||||
|
*/
|
||||||
|
public class ExpansionData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y position of top of first view in section.
|
||||||
|
*/
|
||||||
|
public float sectionStart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y position of bottom of last view in section.
|
||||||
|
*/
|
||||||
|
public float sectionEnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y position of view when shade is fully expanded.
|
||||||
|
* Does not include distance between top notifications panel and top of screen.
|
||||||
|
*/
|
||||||
|
public float fullyExpandedY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this notification is in the same section as the notification right before the
|
||||||
|
* shelf. Used to determine which notification should be clipped to shelf start while
|
||||||
|
* shade expansion changes.
|
||||||
|
*/
|
||||||
|
public boolean inOverflowingSection;
|
||||||
|
}
|
||||||
|
|
||||||
public class StackScrollAlgorithmState {
|
public class StackScrollAlgorithmState {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -648,16 +773,27 @@ public class StackScrollAlgorithm {
|
|||||||
*/
|
*/
|
||||||
public int scrollY;
|
public int scrollY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First view in shelf.
|
||||||
|
*/
|
||||||
|
public ExpandableView firstViewInShelf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First view in section overflowing into shelf while shade expansion changes.
|
||||||
|
*/
|
||||||
|
public ExpandableView firstViewInOverflowSection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of view to ExpansionData used for layout during shade expansion.
|
||||||
|
* Use view instead of index as key, because visibleChildren indices do not match the ones
|
||||||
|
* used in the shelf.
|
||||||
|
*/
|
||||||
|
public Map<ExpandableView, ExpansionData> expansionData = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The children from the host view which are not gone.
|
* The children from the host view which are not gone.
|
||||||
*/
|
*/
|
||||||
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
|
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<>();
|
||||||
|
|
||||||
private int indexOfExpandingNotification;
|
|
||||||
|
|
||||||
public int getIndexOfExpandingNotification() {
|
|
||||||
return indexOfExpandingNotification;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user