Merge "Merge changes I0d79642d,If8756ac8,I29e73897,Ia8c5121a,I533b8f06 into rvc-dev am: 9bad85af2b am: 3bd1a83e56" into rvc-d1-dev-plus-aosp

This commit is contained in:
Automerger Merge Worker
2020-03-31 20:22:40 +00:00
committed by Android (Google) Code Review
26 changed files with 662 additions and 266 deletions

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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>

View File

@@ -3896,6 +3896,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 -->

View File

@@ -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();

View File

@@ -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,16 +537,107 @@ 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.
handleCustomTransformHeight(view, expandingAnimated, iconState);
float fullTransitionAmount;
float transitionAmount;
float contentTransformationAmount;
float shelfStart = getTranslationY();
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(
fullAmount);
interpolatedAmount = NotificationUtils.interpolate(
interpolatedAmount, fullAmount, expandAmount);
fullTransitionAmount = 1.0f - interpolatedAmount;
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,
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,
@@ -573,45 +665,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
}
}
float fullTransitionAmount;
float transitionAmount;
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) {
float fullAmount = (shelfStart - viewStart) / fullHeight;
fullAmount = Math.min(1.0f, fullAmount);
float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
fullAmount);
interpolatedAmount = NotificationUtils.interpolate(
interpolatedAmount, fullAmount, expandAmount);
fullTransitionAmount = 1.0f - interpolatedAmount;
transitionAmount = (shelfStart - viewStart) / iconTransformDistance;
transitionAmount = Math.min(1.0f, transitionAmount);
transitionAmount = 1.0f - transitionAmount;
fullyInOrOut = false;
} else {
fullTransitionAmount = 1.0f;
transitionAmount = 1.0f;
}
} else {
fullTransitionAmount = 0.0f;
transitionAmount = 0.0f;
}
if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
iconState.isLastExpandIcon = false;
iconState.customTransformHeight = NO_VALUE;
}
updateIconPositioning(view, transitionAmount, fullTransitionAmount,
iconTransformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
return fullTransitionAmount;
}
private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
@@ -619,61 +672,69 @@ public class NotificationShelf extends ActivatableNotificationView implements
boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
float contentTransformationAmount;
if (iconState == null) {
contentTransformationAmount = 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);
return;
}
view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
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 {
// 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;
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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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,39 +1520,16 @@ 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);
mChildrenContainer.setShelfIconVisible(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;
}
}
return topPadding;
}
public float getContentTranslation() {
return mPrivateLayout.getTranslationY();
}
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
mPrivateLayout.setIsLowPriority(isLowPriority);
@@ -2134,7 +2100,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
public StatusBarIconView getShelfIcon() {
public @NonNull StatusBarIconView getShelfIcon() {
return getEntry().getIcons().getShelfIcon();
}

View File

@@ -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);
@@ -635,6 +636,56 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
return null;
}
/**
* @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.
*
@@ -665,6 +716,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
if (mIsLastChild) {
translationY *= 0.4f;
}
mContentTranslation = translationY;
applyContentTransformation(contentAlpha, translationY);
}
@@ -708,6 +760,13 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
/**
* return the amount that the content is translated
*/
public float getContentTranslation() {
return mContentTranslation;
}
/**
* A listener notifying when {@link #getActualHeight} changes.
*/

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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;
@@ -198,6 +199,22 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
return mNotificationHeader;
}
@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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -104,7 +104,7 @@ public class StackStateAnimator {
}
@Override
public AnimatorListenerAdapter getAnimationFinishListener() {
public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
return getGlobalAnimationFinishedListener();
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -93,7 +93,7 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Override
public AnimatorListenerAdapter getAnimationFinishListener() {
public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
return mFinishListener;
}
}.setDuration(200);