summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Notification.java2
-rw-r--r--core/java/android/view/NotificationHeaderView.java8
-rw-r--r--core/java/com/android/internal/widget/CachingIconView.java49
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java105
-rw-r--r--core/java/com/android/internal/widget/MessagingPropertyAnimator.java2
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml9
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/Interpolators.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java303
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java2
26 files changed, 656 insertions, 260 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e137acf4dc70..9b42fa2012e1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5817,7 +5817,7 @@ public class Notification implements Parcelable
PorterDuff.Mode.SRC_ATOP);
}
- contentView.setInt(R.id.notification_header, "setOriginalIconColor",
+ contentView.setInt(R.id.icon, "setOriginalIconColor",
colorable ? color : NotificationHeaderView.NO_COLOR);
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 18e0132e2c4e..0359f3b4fde7 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -65,7 +65,6 @@ public class NotificationHeaderView extends ViewGroup {
private View mMicIcon;
private View mAppOps;
private View mAudiblyAlertedIcon;
- private int mIconColor;
private boolean mExpanded;
private boolean mShowExpandButtonAtEnd;
private boolean mShowWorkBadgeAtEnd;
@@ -315,13 +314,8 @@ public class NotificationHeaderView extends ViewGroup {
updateTouchListener();
}
- @RemotableViewMethod
- public void setOriginalIconColor(int color) {
- mIconColor = color;
- }
-
public int getOriginalIconColor() {
- return mIconColor;
+ return mIcon.getOriginalIconColor();
}
public int getOriginalNotificationColor() {
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index bd0623e1144e..84cde1b84e14 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -46,6 +46,9 @@ public class CachingIconView extends ImageView {
private boolean mForceHidden;
private int mDesiredVisibility;
private Consumer<Integer> mOnVisibilityChangedListener;
+ private Consumer<Boolean> mOnForceHiddenChangedListener;
+ private int mIconColor;
+ private boolean mWillBeForceHidden;
@UnsupportedAppUsage
public CachingIconView(Context context, @Nullable AttributeSet attrs) {
@@ -184,10 +187,18 @@ public class CachingIconView extends ImageView {
/**
* Set the icon to be forcibly hidden, even when it's visibility is changed to visible.
+ * This is necessary since we still want to keep certain views hidden when their visibility
+ * is modified from other sources like the shelf.
*/
public void setForceHidden(boolean forceHidden) {
- mForceHidden = forceHidden;
- updateVisibility();
+ if (forceHidden != mForceHidden) {
+ mForceHidden = forceHidden;
+ mWillBeForceHidden = false;
+ updateVisibility();
+ if (mOnForceHiddenChangedListener != null) {
+ mOnForceHiddenChangedListener.accept(forceHidden);
+ }
+ }
}
@Override
@@ -209,4 +220,38 @@ public class CachingIconView extends ImageView {
public void setOnVisibilityChangedListener(Consumer<Integer> listener) {
mOnVisibilityChangedListener = listener;
}
+
+ public void setOnForceHiddenChangedListener(Consumer<Boolean> listener) {
+ mOnForceHiddenChangedListener = listener;
+ }
+
+
+ public boolean isForceHidden() {
+ return mForceHidden;
+ }
+
+ @RemotableViewMethod
+ public void setOriginalIconColor(int color) {
+ mIconColor = color;
+ }
+
+ public int getOriginalIconColor() {
+ return mIconColor;
+ }
+
+ /**
+ * @return if the view will be forceHidden after an animation
+ */
+ public boolean willBeForceHidden() {
+ return mWillBeForceHidden;
+ }
+
+ /**
+ * Set that this view will be force hidden after an animation
+ *
+ * @param forceHidden if it will be forcehidden
+ */
+ public void setWillBeForceHidden(boolean forceHidden) {
+ mWillBeForceHidden = forceHidden;
+ }
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index bedf55d52391..1336ec412cdb 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -18,6 +18,8 @@ package com.android.internal.widget;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
import android.annotation.AttrRes;
import android.annotation.NonNull;
@@ -106,7 +108,7 @@ public class ConversationLayout extends FrameLayout
private CharSequence mNameReplacement;
private boolean mIsCollapsed;
private ImageResolver mImageResolver;
- private ImageView mConversationIcon;
+ private CachingIconView mConversationIcon;
private View mConversationIconContainer;
private int mConversationIconTopPadding;
private int mConversationIconTopPaddingExpandedGroup;
@@ -114,7 +116,7 @@ public class ConversationLayout extends FrameLayout
private int mExpandedGroupMessagePaddingNoAppName;
private TextView mConversationText;
private View mConversationIconBadge;
- private ImageView mConversationIconBadgeBg;
+ private CachingIconView mConversationIconBadgeBg;
private Icon mLargeIcon;
private View mExpandButtonContainer;
private ViewGroup mExpandButtonAndContentContainer;
@@ -125,7 +127,7 @@ public class ConversationLayout extends FrameLayout
private int mConversationAvatarSize;
private int mConversationAvatarSizeExpanded;
private CachingIconView mIcon;
- private View mImportanceRingView;
+ private CachingIconView mImportanceRingView;
private int mExpandedGroupSideMargin;
private int mExpandedGroupSideMarginFacePile;
private View mConversationFacePile;
@@ -140,11 +142,15 @@ public class ConversationLayout extends FrameLayout
private int mContentMarginEnd;
private Rect mMessagingClipRect;
private ObservableTextView mAppName;
+ private ViewGroup mActions;
+ private int mConversationContentStart;
+ private int mInternalButtonPadding;
private boolean mAppNameGone;
private int mFacePileAvatarSize;
private int mFacePileAvatarSizeExpandedGroup;
private int mFacePileProtectionWidth;
private int mFacePileProtectionWidthExpanded;
+ private boolean mImportantConversation;
public ConversationLayout(@NonNull Context context) {
super(context);
@@ -168,6 +174,7 @@ public class ConversationLayout extends FrameLayout
protected void onFinishInflate() {
super.onFinishInflate();
mMessagingLinearLayout = findViewById(R.id.notification_messaging);
+ mActions = findViewById(R.id.actions);
mMessagingLinearLayout.setMessagingLayout(this);
mImageMessageContainer = findViewById(R.id.conversation_image_message_container);
// We still want to clip, but only on the top, since views can temporarily out of bounds
@@ -186,9 +193,41 @@ public class ConversationLayout extends FrameLayout
mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg);
mIcon.setOnVisibilityChangedListener((visibility) -> {
- // Always keep the badge visibility in sync with the icon. This is necessary in cases
- // Where the icon is being hidden externally like in group children.
- mConversationIconBadge.setVisibility(visibility);
+
+ // Let's hide the background directly or in an animated way
+ boolean isGone = visibility == GONE;
+ int oldVisibility = mConversationIconBadgeBg.getVisibility();
+ boolean wasGone = oldVisibility == GONE;
+ if (wasGone != isGone) {
+ // Keep the badge gone state in sync with the icon. This is necessary in cases
+ // Where the icon is being hidden externally like in group children.
+ mConversationIconBadgeBg.animate().cancel();
+ mConversationIconBadgeBg.setVisibility(visibility);
+ }
+
+ // Let's handle the importance ring which can also be be gone normally
+ oldVisibility = mImportanceRingView.getVisibility();
+ wasGone = oldVisibility == GONE;
+ visibility = !mImportantConversation ? GONE : visibility;
+ isGone = visibility == GONE;
+ if (wasGone != isGone) {
+ // Keep the badge visibility in sync with the icon. This is necessary in cases
+ // Where the icon is being hidden externally like in group children.
+ mImportanceRingView.animate().cancel();
+ mImportanceRingView.setVisibility(visibility);
+ }
+ });
+ // When the small icon is gone, hide the rest of the badge
+ mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+ animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ animateViewForceHidden(mImportanceRingView, forceHidden);
+ });
+
+ // When the conversation icon is gone, hide the whole badge
+ mConversationIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+ animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ animateViewForceHidden(mImportanceRingView, forceHidden);
+ animateViewForceHidden(mIcon, forceHidden);
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
@@ -238,6 +277,33 @@ public class ConversationLayout extends FrameLayout
mAppName.setOnVisibilityChangedListener((visibility) -> {
onAppNameVisibilityChanged();
});
+ mConversationContentStart = getResources().getDimensionPixelSize(
+ R.dimen.conversation_content_start);
+ mInternalButtonPadding
+ = getResources().getDimensionPixelSize(R.dimen.button_padding_horizontal_material)
+ + getResources().getDimensionPixelSize(R.dimen.button_inset_horizontal_material);
+ }
+
+ private void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
+ boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
+ if (forceHidden == nowForceHidden) {
+ // We are either already forceHidden or will be
+ return;
+ }
+ view.animate().cancel();
+ view.setWillBeForceHidden(forceHidden);
+ view.animate()
+ .scaleX(forceHidden ? 0.5f : 1.0f)
+ .scaleY(forceHidden ? 0.5f : 1.0f)
+ .alpha(forceHidden ? 0.0f : 1.0f)
+ .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
+ .setDuration(160);
+ if (view.getVisibility() != VISIBLE) {
+ view.setForceHidden(forceHidden);
+ } else {
+ view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
+ }
+ view.animate().start();
}
@RemotableViewMethod
@@ -255,9 +321,14 @@ public class ConversationLayout extends FrameLayout
*/
@RemotableViewMethod
public void setIsImportantConversation(boolean isImportantConversation) {
+ mImportantConversation = isImportantConversation;
mImportanceRingView.setVisibility(isImportantConversation ? VISIBLE : GONE);
}
+ public boolean isImportantConversation() {
+ return mImportantConversation;
+ }
+
/**
* Set this layout to show the collapsed representation.
*
@@ -363,7 +434,6 @@ public class ConversationLayout extends FrameLayout
private void updateConversationLayout() {
// Set avatar and name
CharSequence conversationText = mConversationTitle;
- // TODO: display the secondary text somewhere
if (mIsOneToOne) {
// Let's resolve the icon / text from the last sender
mConversationIcon.setVisibility(VISIBLE);
@@ -418,6 +488,27 @@ public class ConversationLayout extends FrameLayout
updateIconPositionAndSize();
updateImageMessages();
updatePaddingsBasedOnContentAvailability();
+ updateActionListPadding();
+ }
+
+ private void updateActionListPadding() {
+ if (mActions == null) {
+ return;
+ }
+ View firstAction = mActions.getChildAt(0);
+ if (firstAction != null) {
+ // Let's visually position the first action where the content starts
+ int paddingStart = mConversationContentStart;
+
+ MarginLayoutParams layoutParams = (MarginLayoutParams) firstAction.getLayoutParams();
+ paddingStart -= layoutParams.getMarginStart();
+ paddingStart -= mInternalButtonPadding;
+
+ mActions.setPaddingRelative(paddingStart,
+ mActions.getPaddingTop(),
+ mActions.getPaddingEnd(),
+ mActions.getPaddingBottom());
+ }
}
private void updateImageMessages() {
diff --git a/core/java/com/android/internal/widget/MessagingPropertyAnimator.java b/core/java/com/android/internal/widget/MessagingPropertyAnimator.java
index 7703cb0f13db..a3a75c098a00 100644
--- a/core/java/com/android/internal/widget/MessagingPropertyAnimator.java
+++ b/core/java/com/android/internal/widget/MessagingPropertyAnimator.java
@@ -32,7 +32,7 @@ import com.android.internal.R;
*/
public class MessagingPropertyAnimator implements View.OnLayoutChangeListener {
private static final long APPEAR_ANIMATION_LENGTH = 210;
- private static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
private static final int TAG_TOP_ANIMATOR = R.id.tag_top_animator;
private static final int TAG_TOP = R.id.tag_top_override;
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 7cadecbbdb91..46d3d1326920 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -42,7 +42,7 @@
>
<!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@+id/conversation_icon"
android:layout_width="@dimen/conversation_avatar_size"
android:layout_height="@dimen/conversation_avatar_size"
@@ -64,11 +64,12 @@
android:layout_marginLeft="@dimen/conversation_badge_side_margin"
android:layout_marginTop="@dimen/conversation_badge_side_margin"
>
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@+id/conversation_icon_badge_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/conversation_badge_background"
+ android:forceHasOverlappingRendering="false"
/>
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
@@ -76,13 +77,15 @@
android:layout_height="match_parent"
android:layout_margin="4dp"
android:layout_gravity="center"
+ android:forceHasOverlappingRendering="false"
/>
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@+id/conversation_icon_badge_ring"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/conversation_badge_ring"
android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
/>
</FrameLayout>
</FrameLayout>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 04c6a41833df..0fea372ea580 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3895,6 +3895,8 @@
<java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" />
<java-symbol type="dimen" name="conversation_icon_container_top_padding_no_app_name" />
<java-symbol type="layout" name="notification_template_material_conversation" />
+ <java-symbol type="dimen" name="button_padding_horizontal_material" />
+ <java-symbol type="dimen" name="button_inset_horizontal_material" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
<!-- Intent resolver and share sheet -->
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 2b3ea3ab56c0..6923079dd5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -49,6 +49,8 @@ public class Interpolators {
public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
public static final Interpolator HEADS_UP_APPEAR = new HeadsUpAppearInterpolator();
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ public static final Interpolator ICON_OVERSHOT_LESS
+ = new PathInterpolator(0.4f, 0f, 0.2f, 1.1f);
public static final Interpolator PANEL_CLOSE_ACCELERATED
= new PathInterpolator(0.3f, 0, 0.5f, 1);
public static final Interpolator BOUNCE = new BounceInterpolator();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 651623b45f6a..d798692879f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -27,6 +27,7 @@ import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
@@ -536,55 +537,39 @@ public class NotificationShelf extends ActivatableNotificationView implements
// Let calculate how much the view is in the shelf
float viewStart = view.getTranslationY();
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
- float iconTransformDistance = getIntrinsicHeight() * 1.5f;
- iconTransformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
- iconTransformDistance = Math.min(iconTransformDistance, fullHeight);
+ float iconTransformStart = calculateIconTransformationStart(view);
+
+ float transformDistance = getIntrinsicHeight() * 1.5f;
+ transformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
+ transformDistance = Math.min(transformDistance, fullHeight);
+
+ // Let's make sure the transform distance is
+ // at most to the icon (relevant for conversations)
+ transformDistance = Math.min(viewStart + fullHeight - iconTransformStart,
+ transformDistance);
+
if (isLastChild) {
fullHeight = Math.min(fullHeight, view.getMinHeight() - getIntrinsicHeight());
- iconTransformDistance = Math.min(iconTransformDistance, view.getMinHeight()
+ transformDistance = Math.min(transformDistance, view.getMinHeight()
- getIntrinsicHeight());
}
float viewEnd = viewStart + fullHeight;
- // TODO: fix this check for anchor scrolling.
- if (iconState != null && expandingAnimated && mAmbientState.getScrollY() == 0
- && !mAmbientState.isOnKeyguard() && !iconState.isLastExpandIcon) {
- // We are expanding animated. Because we switch to a linear interpolation in this case,
- // the last icon may be stuck in between the shelf position and the notification
- // position, which looks pretty bad. We therefore optimize this case by applying a
- // shorter transition such that the icon is either fully in the notification or we clamp
- // it into the shelf if it's close enough.
- // We need to persist this, since after the expansion, the behavior should still be the
- // same.
- float position = mAmbientState.getIntrinsicPadding()
- + mHostLayout.getPositionInLinearLayout(view);
- int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
- if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart
- && view.getTranslationY() < position) {
- iconState.isLastExpandIcon = true;
- iconState.customTransformHeight = NO_VALUE;
- // Let's check if we're close enough to snap into the shelf
- boolean forceInShelf = mMaxLayoutHeight - getIntrinsicHeight() - position
- < getIntrinsicHeight();
- if (!forceInShelf) {
- // We are overlapping the shelf but not enough, so the icon needs to be
- // repositioned
- iconState.customTransformHeight = (int) (mMaxLayoutHeight
- - getIntrinsicHeight() - position);
- }
- }
- }
+ handleCustomTransformHeight(view, expandingAnimated, iconState);
+
float fullTransitionAmount;
float transitionAmount;
+ float contentTransformationAmount;
float shelfStart = getTranslationY();
- if (iconState != null && iconState.hasCustomTransformHeight()) {
- fullHeight = iconState.customTransformHeight;
- iconTransformDistance = iconState.customTransformHeight;
- }
boolean fullyInOrOut = true;
if (viewEnd >= shelfStart && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
&& (mAmbientState.isShadeExpanded()
|| (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
if (viewStart < shelfStart) {
+ if (iconState != null && iconState.hasCustomTransformHeight()) {
+ fullHeight = iconState.customTransformHeight;
+ transformDistance = iconState.customTransformHeight;
+ }
+
float fullAmount = (shelfStart - viewStart) / fullHeight;
fullAmount = Math.min(1.0f, fullAmount);
float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
@@ -593,87 +578,163 @@ public class NotificationShelf extends ActivatableNotificationView implements
interpolatedAmount, fullAmount, expandAmount);
fullTransitionAmount = 1.0f - interpolatedAmount;
- transitionAmount = (shelfStart - viewStart) / iconTransformDistance;
- transitionAmount = Math.min(1.0f, transitionAmount);
+ if (isLastChild) {
+ // If it's the last child we should use all of the notification to transform
+ // instead of just to the icon, since that can be quite low.
+ transitionAmount = (shelfStart - viewStart) / transformDistance;
+ } else {
+ transitionAmount = (shelfStart - iconTransformStart) / transformDistance;
+ }
+ transitionAmount = MathUtils.constrain(transitionAmount, 0.0f, 1.0f);
transitionAmount = 1.0f - transitionAmount;
fullyInOrOut = false;
} else {
fullTransitionAmount = 1.0f;
transitionAmount = 1.0f;
}
+
+ // Transforming the content
+ contentTransformationAmount = (shelfStart - viewStart) / transformDistance;
+ contentTransformationAmount = Math.min(1.0f, contentTransformationAmount);
+ contentTransformationAmount = 1.0f - contentTransformationAmount;
} else {
fullTransitionAmount = 0.0f;
transitionAmount = 0.0f;
+ contentTransformationAmount = 0.0f;
}
if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
iconState.isLastExpandIcon = false;
iconState.customTransformHeight = NO_VALUE;
}
+
+ // Update the content transformation amount
+ if (view.isAboveShelf() || view.showingPulsing()
+ || (!isLastChild && iconState != null && !iconState.translateContent)) {
+ contentTransformationAmount = 0.0f;
+ }
+ view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
+
+ // Update the positioning of the icon
updateIconPositioning(view, transitionAmount, fullTransitionAmount,
- iconTransformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
+ transformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
+
return fullTransitionAmount;
}
+ /**
+ * @return the location where the transformation into the shelf should start.
+ */
+ private float calculateIconTransformationStart(ExpandableView view) {
+ View target = view.getShelfTransformationTarget();
+ if (target == null) {
+ return view.getTranslationY();
+ }
+ float start = view.getTranslationY() + view.getRelativeTopPadding(target);
+
+ // Let's not start the transformation right at the icon but by the padding before it.
+ start -= view.getShelfIcon().getTop();
+ return start;
+ }
+
+ private void handleCustomTransformHeight(ExpandableView view, boolean expandingAnimated,
+ NotificationIconContainer.IconState iconState) {
+ if (iconState != null && expandingAnimated && mAmbientState.getScrollY() == 0
+ && !mAmbientState.isOnKeyguard() && !iconState.isLastExpandIcon) {
+ // We are expanding animated. Because we switch to a linear interpolation in this case,
+ // the last icon may be stuck in between the shelf position and the notification
+ // position, which looks pretty bad. We therefore optimize this case by applying a
+ // shorter transition such that the icon is either fully in the notification or we clamp
+ // it into the shelf if it's close enough.
+ // We need to persist this, since after the expansion, the behavior should still be the
+ // same.
+ float position = mAmbientState.getIntrinsicPadding()
+ + mHostLayout.getPositionInLinearLayout(view);
+ int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
+ if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart
+ && view.getTranslationY() < position) {
+ iconState.isLastExpandIcon = true;
+ iconState.customTransformHeight = NO_VALUE;
+ // Let's check if we're close enough to snap into the shelf
+ boolean forceInShelf = mMaxLayoutHeight - getIntrinsicHeight() - position
+ < getIntrinsicHeight();
+ if (!forceInShelf) {
+ // We are overlapping the shelf but not enough, so the icon needs to be
+ // repositioned
+ iconState.customTransformHeight = (int) (mMaxLayoutHeight
+ - getIntrinsicHeight() - position);
+ }
+ }
+ }
+ }
+
private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
float fullTransitionAmount, float iconTransformDistance, boolean scrolling,
boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
- float contentTransformationAmount;
if (iconState == null) {
- contentTransformationAmount = iconTransitionAmount;
+ return;
+ }
+ boolean forceInShelf =
+ iconState.isLastExpandIcon && !iconState.hasCustomTransformHeight();
+ boolean clampInShelf = iconTransitionAmount > 0.5f || isTargetClipped(view);
+ float clampedAmount = clampInShelf ? 1.0f : 0.0f;
+ if (iconTransitionAmount == clampedAmount) {
+ iconState.noAnimations = (scrollingFast || expandingAnimated) && !forceInShelf;
+ iconState.useFullTransitionAmount = iconState.noAnimations
+ || (!ICON_ANMATIONS_WHILE_SCROLLING && iconTransitionAmount == 0.0f
+ && scrolling);
+ iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
+ && iconTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging();
+ iconState.translateContent = mMaxLayoutHeight - getTranslationY()
+ - getIntrinsicHeight() > 0;
+ }
+ if (!forceInShelf && (scrollingFast || (expandingAnimated
+ && iconState.useFullTransitionAmount && !ViewState.isAnimatingY(icon)))) {
+ iconState.cancelAnimations(icon);
+ iconState.useFullTransitionAmount = true;
+ iconState.noAnimations = true;
+ }
+ if (iconState.hasCustomTransformHeight()) {
+ iconState.useFullTransitionAmount = true;
+ }
+ if (iconState.isLastExpandIcon) {
+ iconState.translateContent = false;
+ }
+ float transitionAmount;
+ if (mAmbientState.isHiddenAtAll() && !view.isInShelf()) {
+ transitionAmount = mAmbientState.isFullyHidden() ? 1 : 0;
+ } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING
+ || iconState.useFullTransitionAmount
+ || iconState.useLinearTransitionAmount) {
+ transitionAmount = iconTransitionAmount;
} else {
- boolean forceInShelf =
- iconState.isLastExpandIcon && !iconState.hasCustomTransformHeight();
- float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
- if (clampedAmount == fullTransitionAmount) {
- iconState.noAnimations = (scrollingFast || expandingAnimated) && !forceInShelf;
- iconState.useFullTransitionAmount = iconState.noAnimations
- || (!ICON_ANMATIONS_WHILE_SCROLLING && fullTransitionAmount == 0.0f
- && scrolling);
- iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
- && fullTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging();
- iconState.translateContent = mMaxLayoutHeight - getTranslationY()
- - getIntrinsicHeight() > 0;
- }
- if (!forceInShelf && (scrollingFast || (expandingAnimated
- && iconState.useFullTransitionAmount && !ViewState.isAnimatingY(icon)))) {
- iconState.cancelAnimations(icon);
- iconState.useFullTransitionAmount = true;
- iconState.noAnimations = true;
- }
- if (iconState.hasCustomTransformHeight()) {
- iconState.useFullTransitionAmount = true;
- }
- if (iconState.isLastExpandIcon) {
- iconState.translateContent = false;
- }
- float transitionAmount;
- if (mAmbientState.isHiddenAtAll() && !view.isInShelf()) {
- transitionAmount = mAmbientState.isFullyHidden() ? 1 : 0;
- } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- || iconState.useLinearTransitionAmount) {
- transitionAmount = iconTransitionAmount;
- } else {
- // We take the clamped position instead
- transitionAmount = clampedAmount;
- iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
- && !mNoAnimationsInThisFrame;
- }
- iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- ? fullTransitionAmount
- : transitionAmount;
- iconState.clampedAppearAmount = clampedAmount;
- contentTransformationAmount = !view.isAboveShelf() && !view.showingPulsing()
- && (isLastChild || iconState.translateContent)
- ? iconTransitionAmount
- : 0.0f;
- setIconTransformationAmount(view, transitionAmount, iconTransformDistance,
- clampedAmount != transitionAmount, isLastChild);
+ // We take the clamped position instead
+ transitionAmount = clampedAmount;
+ iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
+ && !mNoAnimationsInThisFrame;
}
- view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
+ iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
+ || iconState.useFullTransitionAmount
+ ? fullTransitionAmount
+ : transitionAmount;
+ iconState.clampedAppearAmount = clampedAmount;
+ setIconTransformationAmount(view, transitionAmount, iconTransformDistance,
+ clampedAmount != transitionAmount, isLastChild);
+ }
+
+ private boolean isTargetClipped(ExpandableView view) {
+ View target = view.getShelfTransformationTarget();
+ if (target == null) {
+ return false;
+ }
+ // We should never clip the target, let's instead put it into the shelf!
+ float endOfTarget = view.getTranslationY()
+ + view.getContentTranslation()
+ + view.getRelativeTopPadding(target)
+ + target.getHeight();
+
+ return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
}
private void setIconTransformationAmount(ExpandableView view,
@@ -686,40 +747,63 @@ public class NotificationShelf extends ActivatableNotificationView implements
StatusBarIconView icon = row.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
+ View rowIcon = row.getShelfTransformationTarget();
- View rowIcon = row.getNotificationIcon();
- float notificationIconPosition = row.getTranslationY() + row.getContentTranslation();
- boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
- if (usingLinearInterpolation && !stayingInShelf) {
- // If we interpolate from the notification position, this might lead to a slightly
- // odd interpolation, since the notification position changes as well. Let's interpolate
- // from a fixed distance. We can only do this if we don't animate and the icon is
- // always in the interpolated positon.
- notificationIconPosition = getTranslationY() - iconTransformDistance;
- }
+ // Let's resolve the relative positions of the icons
float notificationIconSize = 0.0f;
int iconTopPadding;
+ int iconStartPadding;
if (rowIcon != null) {
iconTopPadding = row.getRelativeTopPadding(rowIcon);
+ iconStartPadding = row.getRelativeStartPadding(rowIcon);
notificationIconSize = rowIcon.getHeight();
} else {
iconTopPadding = mIconAppearTopPadding;
+ iconStartPadding = 0;
}
- notificationIconPosition += iconTopPadding;
- float shelfIconPosition = getTranslationY() + icon.getTop();
- float iconSize = mAmbientState.isFullyHidden() ? mHiddenShelfIconSize : mIconSize;
- shelfIconPosition += (icon.getHeight() - icon.getIconScale() * iconSize) / 2.0f;
+
+ float shelfIconSize = mAmbientState.isFullyHidden() ? mHiddenShelfIconSize : mIconSize;
+ shelfIconSize = shelfIconSize * icon.getIconScale();
+
+ // Get the icon correctly positioned in Y
+ float notificationIconPositionY = row.getTranslationY() + row.getContentTranslation();
+ float targetYPosition = 0;
+ boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
+ if (usingLinearInterpolation && !stayingInShelf) {
+ // If we interpolate from the notification position, this might lead to a slightly
+ // odd interpolation, since the notification position changes as well.
+ // Let's instead interpolate directly to the top left of the notification
+ targetYPosition = NotificationUtils.interpolate(
+ Math.min(notificationIconPositionY + mIconAppearTopPadding
+ - getTranslationY(), 0),
+ 0,
+ transitionAmount);
+ }
+ notificationIconPositionY += iconTopPadding;
+ float shelfIconPositionY = getTranslationY() + icon.getTop();
+ shelfIconPositionY += (icon.getHeight() - shelfIconSize) / 2.0f;
float iconYTranslation = NotificationUtils.interpolate(
- notificationIconPosition - shelfIconPosition,
- 0,
+ notificationIconPositionY - shelfIconPositionY,
+ targetYPosition,
transitionAmount);
- float shelfIconSize = iconSize * icon.getIconScale();
+
+ // Get the icon correctly positioned in X
+ // Even in RTL it's the left, since we're inverting the location in post
+ float shelfIconPositionX = icon.getLeft();
+ shelfIconPositionX += (1.0f - icon.getIconScale()) * icon.getWidth() / 2.0f;
+ float iconXTranslation = NotificationUtils.interpolate(
+ iconStartPadding - shelfIconPositionX,
+ mShelfIcons.getActualPaddingStart(),
+ transitionAmount);
+
+ // Let's handle the case that there's no Icon
float alpha = 1.0f;
boolean noIcon = !row.isShowingIcon();
if (noIcon) {
// The view currently doesn't have an icon, lets transform it in!
alpha = transitionAmount;
notificationIconSize = shelfIconSize / 2.0f;
+ iconXTranslation = mShelfIcons.getActualPaddingStart();
}
// The notification size is different from the size in the shelf / statusbar
float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
@@ -735,6 +819,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
iconState.alpha = alpha;
iconState.yTranslation = iconYTranslation;
+ iconState.xTranslation = iconXTranslation;
if (stayingInShelf) {
iconState.iconAppearAmount = 1.0f;
iconState.alpha = 1.0f;
@@ -751,7 +836,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
int backgroundColor = getBackgroundColorWithoutTint();
int shelfColor = icon.getContrastedStaticDrawableColor(backgroundColor);
if (!noIcon && shelfColor != StatusBarIconView.NO_COLOR) {
- int iconColor = row.getVisibleNotificationHeader().getOriginalIconColor();
+ int iconColor = row.getOriginalIconColor();
shelfColor = NotificationUtils.interpolateColors(iconColor, shelfColor,
iconState.iconAppearAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 12add8c8d9cc..8cf8a2299922 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -159,7 +159,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
private boolean mDismissed;
private Runnable mOnDismissListener;
private boolean mIncreasedSize;
- private boolean mTintIcons = true;
+ private boolean mShowsConversation;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -613,7 +613,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
private void updateIconColor() {
- if (!mTintIcons) {
+ if (mShowsConversation) {
setColorFilter(null);
return;
}
@@ -913,7 +913,11 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
private void updatePivot() {
- setPivotX((1 - mIconScale) / 2.0f * getWidth());
+ if (isLayoutRtl()) {
+ setPivotX((1 + mIconScale) / 2.0f * getWidth());
+ } else {
+ setPivotX((1 - mIconScale) / 2.0f * getWidth());
+ }
setPivotY((getHeight() - mIconScale * getWidth()) / 2.0f);
}
@@ -960,18 +964,26 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
/**
- * Sets whether the icon should be tinted. If the state differs from the supplied setting, this
+ * Sets whether this icon shows a person and should be tinted.
+ * If the state differs from the supplied setting, this
* will update the icon colors.
*
- * @param shouldTint Whether the icon should be tinted.
+ * @param showsConversation Whether the icon shows a person
*/
- public void setTintIcons(boolean shouldTint) {
- if (mTintIcons != shouldTint) {
- mTintIcons = shouldTint;
+ public void setShowsConversation(boolean showsConversation) {
+ if (mShowsConversation != showsConversation) {
+ mShowsConversation = showsConversation;
updateIconColor();
}
}
+ /**
+ * @return if this icon shows a conversation
+ */
+ public boolean showsConversation() {
+ return mShowsConversation;
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index fe565529db32..1f9d3af70b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -96,7 +96,7 @@ public class PropertyAnimator {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(property);
if (listener != null) {
animator.addListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index bb0fcafdb354..da8ad2da5c87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -100,7 +100,7 @@ class IconManager @Inject constructor(
// TODO: This doesn't belong here
shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
if (entry.row != null) {
- entry.row.setIconsVisible(newVisibility != View.VISIBLE)
+ entry.row.setShelfIconVisible(newVisibility == View.VISIBLE)
}
}
@@ -258,7 +258,7 @@ class IconManager @Inject constructor(
iconDescriptor: StatusBarIcon,
iconView: StatusBarIconView
) {
- iconView.setTintIcons(shouldTintIconView(entry, iconView))
+ iconView.setShowsConversation(showsConversation(entry, iconView, iconDescriptor))
if (!iconView.set(iconDescriptor)) {
throw InflationException("Couldn't create icon $iconDescriptor")
}
@@ -312,15 +312,22 @@ class IconManager @Inject constructor(
}
/**
- * Determines if this icon should be tinted based on the sensitivity of the icon, its context
- * and the user's indicated sensitivity preference.
+ * Determines if this icon shows a conversation based on the sensitivity of the icon, its
+ * context and the user's indicated sensitivity preference. If we're using a fall back icon
+ * of the small icon, we don't consider this to be showing a conversation
*
- * @param iconView The icon that should/should not be tinted.
+ * @param iconView The icon that shows the conversation.
*/
- private fun shouldTintIconView(entry: NotificationEntry, iconView: StatusBarIconView): Boolean {
+ private fun showsConversation(
+ entry: NotificationEntry,
+ iconView: StatusBarIconView,
+ iconDescriptor: StatusBarIcon
+ ): Boolean {
val usedInSensitiveContext =
iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
- return !isImportantConversation(entry) || usedInSensitiveContext && entry.isSensitive
+ val isSmallIcon = iconDescriptor.icon.equals(entry.sbn.notification.smallIcon)
+ return isImportantConversation(entry) && !isSmallIcon
+ && (!usedInSensitiveContext || !entry.isSensitive)
}
private fun isImportantConversation(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0ff058b31527..7deabf79a6dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -31,6 +31,7 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -311,7 +312,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mHeadsupDisappearRunning;
private View mChildAfterViewWhenDismissed;
private View mGroupParentWhenDismissed;
- private boolean mIconsVisible = true;
+ private boolean mShelfIconVisible;
private boolean mAboveShelf;
private Runnable mOnDismissRunnable;
private boolean mIsLowPriority;
@@ -587,17 +588,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
ContrastColorUtil.getInstance(mContext));
int color = StatusBarIconView.NO_COLOR;
if (colorize) {
- NotificationHeaderView header = getVisibleNotificationHeader();
- if (header != null) {
- color = header.getOriginalIconColor();
- } else {
- color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
- getBackgroundColorWithoutTint());
- }
+ color = getOriginalIconColor();
}
expandedIcon.setStaticDrawableColor(color);
}
+ public int getOriginalIconColor() {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
+ return mChildrenContainer.getVisibleHeader().getOriginalIconColor();
+ }
+ int color = getShowingLayout().getOriginalIconColor();
+ if (color != Notification.COLOR_INVALID) {
+ return color;
+ } else {
+ return mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
+ getBackgroundColorWithoutTint());
+ }
+ }
+
public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
mAboveShelfChangedListener = aboveShelfChangedListener;
}
@@ -1452,12 +1460,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mOnDismissRunnable = onDismissRunnable;
}
- public View getNotificationIcon() {
- NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
- if (notificationHeader != null) {
- return notificationHeader.getIcon();
+ @Override
+ public View getShelfTransformationTarget() {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
+ return mChildrenContainer.getVisibleHeader().getIcon();
}
- return null;
+ return getShowingLayout().getShelfTransformationTarget();
}
/**
@@ -1467,34 +1475,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (areGutsExposed()) {
return false;
}
- return getVisibleNotificationHeader() != null;
- }
-
- /**
- * Set how much this notification is transformed into an icon.
- *
- * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
- * to the content away
- * @param isLastChild is this the last child in the list. If true, then the transformation is
- * different since it's content fades out.
- */
- public void setContentTransformationAmount(float contentTransformationAmount,
- boolean isLastChild) {
- boolean changeTransformation = isLastChild != mIsLastChild;
- changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
- mIsLastChild = isLastChild;
- mContentTransformationAmount = contentTransformationAmount;
- if (changeTransformation) {
- updateContentTransformation();
- }
+ return getShelfTransformationTarget() != null;
}
/**
* Set the icons to be visible of this notification.
*/
- public void setIconsVisible(boolean iconsVisible) {
- if (iconsVisible != mIconsVisible) {
- mIconsVisible = iconsVisible;
+ public void setShelfIconVisible(boolean iconVisible) {
+ if (iconVisible != mShelfIconVisible) {
+ mShelfIconVisible = iconVisible;
updateIconVisibilities();
}
}
@@ -1531,37 +1520,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void updateIconVisibilities() {
- boolean visible = isChildInGroup() || mIconsVisible;
+ // The shelficon is never hidden for children in groups
+ boolean visible = !isChildInGroup() && mShelfIconVisible;
for (NotificationContentView l : mLayouts) {
- l.setIconsVisible(visible);
+ l.setShelfIconVisible(visible);
}
if (mChildrenContainer != null) {
- mChildrenContainer.setIconsVisible(visible);
- }
- }
-
- /**
- * Get the relative top padding of a view relative to this view. This recursively walks up the
- * hierarchy and does the corresponding measuring.
- *
- * @param view the view to the the padding for. The requested view has to be a child of this
- * notification.
- * @return the toppadding
- */
- public int getRelativeTopPadding(View view) {
- int topPadding = 0;
- while (view.getParent() instanceof ViewGroup) {
- topPadding += view.getTop();
- view = (View) view.getParent();
- if (view instanceof ExpandableNotificationRow) {
- return topPadding;
- }
+ mChildrenContainer.setShelfIconVisible(visible);
}
- return topPadding;
- }
-
- public float getContentTranslation() {
- return mPrivateLayout.getTranslationY();
}
public void setIsLowPriority(boolean isLowPriority) {
@@ -2134,7 +2100,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public StatusBarIconView getShelfIcon() {
+ public @NonNull StatusBarIconView getShelfIcon() {
return getEntry().getIcons().getShelfIcon();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index a9f72ff9ea62..ee3b753ab926 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -66,6 +66,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
protected boolean mIsLastChild;
protected int mContentShift;
private final ExpandableViewState mViewState;
+ private float mContentTranslation;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -636,6 +637,56 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
}
/**
+ * @return get the transformation target of the shelf, which usually is the icon
+ */
+ public View getShelfTransformationTarget() {
+ return null;
+ }
+
+ /**
+ * Get the relative top padding of a view relative to this view. This recursively walks up the
+ * hierarchy and does the corresponding measuring.
+ *
+ * @param view the view to get the padding for. The requested view has to be a child of this
+ * notification.
+ * @return the toppadding
+ */
+ public int getRelativeTopPadding(View view) {
+ int topPadding = 0;
+ while (view.getParent() instanceof ViewGroup) {
+ topPadding += view.getTop();
+ view = (View) view.getParent();
+ if (view == this) {
+ return topPadding;
+ }
+ }
+ return topPadding;
+ }
+
+
+ /**
+ * Get the relative start padding of a view relative to this view. This recursively walks up the
+ * hierarchy and does the corresponding measuring.
+ *
+ * @param view the view to get the padding for. The requested view has to be a child of this
+ * notification.
+ * @return the start padding
+ */
+ public int getRelativeStartPadding(View view) {
+ boolean isRtl = isLayoutRtl();
+ int startPadding = 0;
+ while (view.getParent() instanceof ViewGroup) {
+ View parent = (View) view.getParent();
+ startPadding += isRtl ? parent.getWidth() - view.getRight() : view.getLeft();
+ view = parent;
+ if (view == this) {
+ return startPadding;
+ }
+ }
+ return startPadding;
+ }
+
+ /**
* Set how much this notification is transformed into the shelf.
*
* @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
@@ -665,6 +716,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
if (mIsLastChild) {
translationY *= 0.4f;
}
+ mContentTranslation = translationY;
applyContentTransformation(contentAlpha, translationY);
}
@@ -709,6 +761,13 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
}
/**
+ * return the amount that the content is translated
+ */
+ public float getContentTranslation() {
+ return mContentTranslation;
+ }
+
+ /**
* A listener notifying when {@link #getActualHeight} changes.
*/
public interface OnHeightChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8b8a9012cbdc..9b9225e0bde0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -159,7 +159,7 @@ public class NotificationContentView extends FrameLayout {
private int mContentHeightAtAnimationStart = UNDEFINED;
private boolean mFocusOnVisibilityChange;
private boolean mHeadsUpAnimatingAway;
- private boolean mIconsVisible;
+ private boolean mShelfIconVisible;
private int mClipBottomAmount;
private boolean mIsLowPriority;
private boolean mIsContentExpandable;
@@ -1582,29 +1582,20 @@ public class NotificationContentView extends FrameLayout {
mFocusOnVisibilityChange = true;
}
- public void setIconsVisible(boolean iconsVisible) {
- mIconsVisible = iconsVisible;
+ public void setShelfIconVisible(boolean iconsVisible) {
+ mShelfIconVisible = iconsVisible;
updateIconVisibilities();
}
private void updateIconVisibilities() {
if (mContractedWrapper != null) {
- NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
- if (header != null) {
- header.getIcon().setForceHidden(!mIconsVisible);
- }
+ mContractedWrapper.setShelfIconVisible(mShelfIconVisible);
}
if (mHeadsUpWrapper != null) {
- NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
- if (header != null) {
- header.getIcon().setForceHidden(!mIconsVisible);
- }
+ mHeadsUpWrapper.setShelfIconVisible(mShelfIconVisible);
}
if (mExpandedWrapper != null) {
- NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
- if (header != null) {
- header.getIcon().setForceHidden(!mIconsVisible);
- }
+ mExpandedWrapper.setShelfIconVisible(mShelfIconVisible);
}
}
@@ -1835,4 +1826,23 @@ public class NotificationContentView extends FrameLayout {
public RemoteInputView getExpandedRemoteInput() {
return mExpandedRemoteInput;
}
+
+ /**
+ * @return get the transformation target of the shelf, which usually is the icon
+ */
+ public View getShelfTransformationTarget() {
+ NotificationViewWrapper visibleWrapper = getVisibleWrapper(mVisibleType);
+ if (visibleWrapper != null) {
+ return visibleWrapper.getShelfTransformationTarget();
+ }
+ return null;
+ }
+
+ public int getOriginalIconColor() {
+ NotificationViewWrapper visibleWrapper = getVisibleWrapper(mVisibleType);
+ if (visibleWrapper != null) {
+ return visibleWrapper.getOriginalIconColor();
+ }
+ return Notification.COLOR_INVALID;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 593de23c58de..13e7fe5cd6d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.notification.row.wrapper
import android.content.Context
import android.view.View
+import android.view.View.GONE
import android.view.ViewGroup
+import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingLinearLayout
import com.android.systemui.R
@@ -44,7 +46,7 @@ class NotificationConversationTemplateViewWrapper constructor(
)
private val conversationLayout: ConversationLayout = view as ConversationLayout
- private lateinit var conversationIcon: View
+ private lateinit var conversationIcon: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandButton: View
private lateinit var expandButtonContainer: View
@@ -132,6 +134,32 @@ class NotificationConversationTemplateViewWrapper constructor(
)
}
+ override fun setShelfIconVisible(visible: Boolean) {
+ if (conversationLayout.isImportantConversation) {
+ if (conversationIcon.visibility != GONE) {
+ conversationIcon.setForceHidden(visible);
+ // We don't want the small icon to be hidden by the extended wrapper, as force
+ // hiding the conversationIcon will already do that via its listener.
+ return;
+ }
+ }
+ super.setShelfIconVisible(visible)
+ }
+
+ override fun getShelfTransformationTarget(): View? {
+ if (conversationLayout.isImportantConversation) {
+ if (conversationIcon.visibility != GONE) {
+ return conversationIcon
+ } else {
+ // A notification with a fallback icon was set to important. Currently
+ // the transformation doesn't work for these and needs to be fixed. In the meantime
+ // those are using the icon.
+ return super.getShelfTransformationTarget();
+ }
+ }
+ return super.getShelfTransformationTarget()
+ }
+
override fun setRemoteInputVisible(visible: Boolean) =
conversationLayout.showHistoricMessages(visible)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index a44ad3d7f5c1..7808a4b2dc74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -29,6 +29,7 @@ import android.view.animation.PathInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -54,7 +55,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
protected int mColor;
- private ImageView mIcon;
+ private CachingIconView mIcon;
private NotificationExpandButton mExpandButton;
protected NotificationHeaderView mNotificationHeader;
private TextView mHeaderText;
@@ -199,6 +200,22 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
+ public int getOriginalIconColor() {
+ return mIcon.getOriginalIconColor();
+ }
+
+ @Override
+ public View getShelfTransformationTarget() {
+ return mIcon;
+ }
+
+ @Override
+ public void setShelfIconVisible(boolean visible) {
+ super.setShelfIconVisible(visible);
+ mIcon.setForceHidden(visible);
+ }
+
+ @Override
public TransformState getCurrentState(int fadingView) {
return mTransformationHelper.getCurrentState(fadingView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 46d7d9374290..e4fb2f7c42d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.annotation.ColorInt;
+import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
@@ -230,6 +231,22 @@ public abstract class NotificationViewWrapper implements TransformableView {
return null;
}
+ public int getOriginalIconColor() {
+ return Notification.COLOR_INVALID;
+ }
+
+ /**
+ * @return get the transformation target of the shelf, which usually is the icon
+ */
+ public @Nullable View getShelfTransformationTarget() {
+ return null;
+ }
+
+ /**
+ * Set the shelf icon to be visible and hide our own icons.
+ */
+ public void setShelfIconVisible(boolean shelfIconVisible) {}
+
public int getHeaderTranslation(boolean forceNoHeader) {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 87a3cc9d2db8..112d48c115c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.notification.stack;
+import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.util.ArrayMap;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import java.util.function.Consumer;
+
/**
* Properties for a View animation
*/
@@ -29,7 +32,7 @@ public class AnimationProperties {
public long duration;
public long delay;
private ArrayMap<Property, Interpolator> mInterpolatorMap;
- private AnimatorListenerAdapter mAnimatorListenerAdapter;
+ private Consumer<Property> mAnimationEndAction;
/**
* @return an animation filter for this animation.
@@ -44,14 +47,32 @@ public class AnimationProperties {
}
/**
- * @return a listener that should be run whenever any property finished its animation
+ * @return a listener that will be added for a given property during its animation.
*/
- public AnimatorListenerAdapter getAnimationFinishListener() {
- return mAnimatorListenerAdapter;
+ public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
+ if (mAnimationEndAction == null) {
+ return null;
+ }
+ Consumer<Property> endAction = mAnimationEndAction;
+ return new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ endAction.accept(property);
+ }
+ }
+ };
}
- public AnimationProperties setAnimationFinishListener(AnimatorListenerAdapter listener) {
- mAnimatorListenerAdapter = listener;
+ public AnimationProperties setAnimationEndAction(Consumer<Property> listener) {
+ mAnimationEndAction = listener;
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index 72ef7f9572a4..628c4e258e50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -263,7 +263,8 @@ public class ExpandableViewState extends ViewState {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(
+ null /* no property for this height */);
if (listener != null) {
animator.addListener(listener);
}
@@ -343,7 +344,8 @@ public class ExpandableViewState extends ViewState {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(
+ null /* no property for top inset */);
if (listener != null) {
animator.addListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 2c17764799a5..3d0bf3f4c1c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1187,18 +1187,18 @@ public class NotificationChildrenContainer extends ViewGroup {
return 0;
}
- public void setIconsVisible(boolean iconsVisible) {
+ public void setShelfIconVisible(boolean iconVisible) {
if (mNotificationHeaderWrapper != null) {
NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
if (header != null) {
- header.getIcon().setForceHidden(!iconsVisible);
+ header.getIcon().setForceHidden(iconVisible);
}
}
if (mNotificationHeaderWrapperLowPriority != null) {
NotificationHeaderView header
= mNotificationHeaderWrapperLowPriority.getNotificationHeader();
if (header != null) {
- header.getIcon().setForceHidden(!iconsVisible);
+ header.getIcon().setForceHidden(iconVisible);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 14442e346db4..77850826a5e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -104,7 +104,7 @@ public class StackStateAnimator {
}
@Override
- public AnimatorListenerAdapter getAnimationFinishListener() {
+ public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
return getGlobalAnimationFinishedListener();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index b00068cd2445..3da4e321c54d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -393,7 +393,7 @@ public class ViewState implements Dumpable {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(View.ALPHA);
if (listener != null) {
animator.addListener(listener);
}
@@ -450,7 +450,8 @@ public class ViewState implements Dumpable {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(
+ View.TRANSLATION_Z);
if (listener != null) {
animator.addListener(listener);
}
@@ -515,7 +516,8 @@ public class ViewState implements Dumpable {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(
+ View.TRANSLATION_X);
if (listener != null) {
animator.addListener(listener);
}
@@ -580,7 +582,8 @@ public class ViewState implements Dumpable {
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+ AnimatorListenerAdapter listener = properties.getAnimationFinishListener(
+ View.TRANSLATION_Y);
if (listener != null) {
animator.addListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index a53ce9bb2014..07eaaa187fbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -27,7 +29,9 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
+import android.util.Property;
import android.view.View;
+import android.view.animation.Interpolator;
import androidx.collection.ArrayMap;
@@ -42,6 +46,7 @@ import com.android.systemui.statusbar.notification.stack.ViewState;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.function.Consumer;
/**
* A container for notification icons. It handles overflowing icons properly and positions them
@@ -69,7 +74,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}.setDuration(200);
private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha()
+ private AnimationFilter mAnimationFilter = new AnimationFilter()
+ .animateX()
+ .animateY()
+ .animateAlpha()
.animateScale();
@Override
@@ -77,8 +85,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
return mAnimationFilter;
}
- }.setDuration(CANNED_ANIMATION_DURATION)
- .setCustomInterpolator(View.TRANSLATION_Y, Interpolators.ICON_OVERSHOT);
+ }.setDuration(CANNED_ANIMATION_DURATION);
/**
* Temporary AnimationProperties to avoid unnecessary allocations.
@@ -272,7 +279,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
super.onViewAdded(child);
boolean isReplacingIcon = isReplacingIcon(child);
if (!mChangingViewPositions) {
- IconState v = new IconState();
+ IconState v = new IconState(child);
if (isReplacingIcon) {
v.justAdded = false;
v.justReplaced = true;
@@ -388,7 +395,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
IconState iconState = mIconStates.get(view);
- iconState.xTranslation = translationX;
+ if (iconState.iconAppearAmount == 1.0f) {
+ // We only modify the xTranslation if it's fully inside of the container
+ // since during the transition to the shelf, the translations are controlled
+ // from the outside
+ iconState.xTranslation = translationX;
+ }
if (mFirstVisibleIconState == null) {
mFirstVisibleIconState = iconState;
}
@@ -499,7 +511,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
return mActualPaddingEnd;
}
- private float getActualPaddingStart() {
+ /**
+ * @return the actual startPadding of this view
+ */
+ public float getActualPaddingStart() {
if (mActualPaddingStart == NO_VALUE) {
return getPaddingStart();
}
@@ -692,6 +707,20 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
public boolean noAnimations;
public boolean isLastExpandIcon;
public int customTransformHeight = NO_VALUE;
+ private final View mView;
+
+ private final Consumer<Property> mCannedAnimationEndListener;
+
+ public IconState(View child) {
+ mView = child;
+ mCannedAnimationEndListener = (property) -> {
+ // If we finished animating out of the shelf
+ if (property == View.TRANSLATION_Y && iconAppearAmount == 0.0f
+ && mView.getVisibility() == VISIBLE) {
+ mView.setVisibility(INVISIBLE);
+ }
+ };
+ }
@Override
public void applyToView(View view) {
@@ -729,6 +758,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
ICON_ANIMATION_PROPERTIES.getAnimationFilter());
sTempProperties.resetCustomInterpolators();
sTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
+ Interpolator interpolator;
+ if (icon.showsConversation()) {
+ interpolator = Interpolators.ICON_OVERSHOT_LESS;
+ } else {
+ interpolator = Interpolators.ICON_OVERSHOT;
+ }
+ sTempProperties.setCustomInterpolator(View.TRANSLATION_Y, interpolator);
+ sTempProperties.setAnimationEndAction(mCannedAnimationEndListener);
if (animationProperties != null) {
animationFilter.combineFilter(animationProperties.getAnimationFilter());
sTempProperties.combineCustomInterpolators(animationProperties);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 5588c24f2fd6..98ba6e5b88a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -379,14 +379,6 @@ public class NotificationPanelViewController extends PanelViewController {
private Runnable mPanelAlphaEndAction;
private float mBottomAreaShadeAlpha;
private final ValueAnimator mBottomAreaShadeAlphaAnimator;
- private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mPanelAlphaEndAction != null) {
- mPanelAlphaEndAction.run();
- }
- }
- };
private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
NotificationPanelView::setPanelAlphaInternal,
NotificationPanelView::getCurrentPanelAlpha,
@@ -396,8 +388,11 @@ public class NotificationPanelViewController extends PanelViewController {
new AnimationProperties().setDuration(150).setCustomInterpolator(
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
private final AnimationProperties mPanelAlphaInPropertiesAnimator =
- new AnimationProperties().setDuration(200).setAnimationFinishListener(
- mAnimatorListenerAdapter).setCustomInterpolator(
+ new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
+ if (mPanelAlphaEndAction != null) {
+ mPanelAlphaEndAction.run();
+ }
+ }).setCustomInterpolator(
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index 6359234fa6ba..57278e32d553 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -93,7 +93,7 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Override
- public AnimatorListenerAdapter getAnimationFinishListener() {
+ public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
return mFinishListener;
}
}.setDuration(200);