diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 4e7ae8a235e78..c28e3ad5052ef 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -93,7 +93,6 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mMessagingLinearLayout; private boolean mShowHistoricMessages; private ArrayList mGroups = new ArrayList<>(); - private TextView mTitleView; private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; @@ -108,6 +107,11 @@ public class ConversationLayout extends FrameLayout private boolean mIsCollapsed; private ImageResolver mImageResolver; private ImageView mConversationIcon; + private View mConversationIconContainer; + private int mConversationIconTopPadding; + private int mConversationIconTopPaddingExpandedGroup; + private int mConversationIconTopPaddingNoAppName; + private int mExpandedGroupMessagePaddingNoAppName; private TextView mConversationText; private View mConversationIconBadge; private Icon mLargeIcon; @@ -117,11 +121,12 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mImageMessageContainer; private int mExpandButtonExpandedTopMargin; private int mBadgedSideMargins; - private int mIconSizeBadged; - private int mIconSizeCentered; + private int mConversationAvatarSize; + private int mConversationAvatarSizeExpanded; private CachingIconView mIcon; private View mImportanceRingView; - private int mExpandedGroupTopMargin; + private int mExpandedGroupSideMargin; + private int mExpandedGroupSideMarginFacePile; private View mConversationFacePile; private int mNotificationBackgroundColor; private CharSequence mFallbackChatName; @@ -133,7 +138,12 @@ public class ConversationLayout extends FrameLayout private boolean mExpandable = true; private int mContentMarginEnd; private Rect mMessagingClipRect; - private TextView mAppName; + private ObservableTextView mAppName; + private boolean mAppNameGone; + private int mFacePileAvatarSize; + private int mFacePileAvatarSizeExpandedGroup; + private int mFacePileProtectionWidth; + private int mFacePileProtectionWidthExpanded; public ConversationLayout(@NonNull Context context) { super(context); @@ -165,11 +175,11 @@ public class ConversationLayout extends FrameLayout int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); mMessagingClipRect = new Rect(0, 0, size, size); setMessagingClippingDisabled(false); - mTitleView = findViewById(R.id.title); mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setAntiAlias(true); mConversationIcon = findViewById(R.id.conversation_icon); + mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); mConversationIconBadge = findViewById(R.id.conversation_icon_badge); @@ -192,18 +202,40 @@ public class ConversationLayout extends FrameLayout R.dimen.notification_content_margin_end); mBadgedSideMargins = getResources().getDimensionPixelSize( R.dimen.conversation_badge_side_margin); - mIconSizeBadged = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_size_badged); - mIconSizeCentered = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_size_centered); - mExpandedGroupTopMargin = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_margin_top_centered); + mConversationAvatarSize = getResources().getDimensionPixelSize( + R.dimen.conversation_avatar_size); + mConversationAvatarSizeExpanded = getResources().getDimensionPixelSize( + R.dimen.conversation_avatar_size_group_expanded); + mConversationIconTopPadding = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding); + mConversationIconTopPaddingExpandedGroup = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding_small_avatar); + mConversationIconTopPaddingNoAppName = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding_no_app_name); + mExpandedGroupMessagePaddingNoAppName = getResources().getDimensionPixelSize( + R.dimen.expanded_group_conversation_message_padding_without_app_name); + mExpandedGroupSideMargin = getResources().getDimensionPixelSize( + R.dimen.conversation_badge_side_margin_group_expanded); + mExpandedGroupSideMarginFacePile = getResources().getDimensionPixelSize( + R.dimen.conversation_badge_side_margin_group_expanded_face_pile); mConversationFacePile = findViewById(R.id.conversation_face_pile); + mFacePileAvatarSize = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_avatar_size); + mFacePileAvatarSizeExpandedGroup = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_avatar_size_group_expanded); + mFacePileProtectionWidth = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_protection_width); + mFacePileProtectionWidthExpanded = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_protection_width_expanded); mFallbackChatName = getResources().getString( R.string.conversation_title_fallback_one_to_one); mFallbackGroupChatName = getResources().getString( R.string.conversation_title_fallback_group_chat); mAppName = findViewById(R.id.app_name_text); + mAppNameGone = mAppName.getVisibility() == GONE; + mAppName.setOnVisibilityChangedListener((visibility) -> { + onAppNameVisibilityChanged(); + }); } @RemotableViewMethod @@ -234,7 +266,7 @@ public class ConversationLayout extends FrameLayout mIsCollapsed = isCollapsed; mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); updateExpandButton(); - updateContentPaddings(); + updateContentEndPaddings(); } @RemotableViewMethod @@ -354,21 +386,17 @@ public class ConversationLayout extends FrameLayout } } } else { - if (mIsCollapsed) { - if (mLargeIcon != null) { - mConversationIcon.setVisibility(VISIBLE); - mConversationFacePile.setVisibility(GONE); - mConversationIcon.setImageIcon(mLargeIcon); - } else { - mConversationIcon.setVisibility(GONE); - // This will also inflate it! - mConversationFacePile.setVisibility(VISIBLE); - mConversationFacePile = findViewById(R.id.conversation_face_pile); - bindFacePile(); - } - } else { + if (mLargeIcon != null) { + mConversationIcon.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); + mConversationIcon.setImageIcon(mLargeIcon); + } else { mConversationIcon.setVisibility(GONE); + // This will also inflate it! + mConversationFacePile.setVisibility(VISIBLE); + // rebind the value to the inflated view instead of the stub + mConversationFacePile = findViewById(R.id.conversation_face_pile); + bindFacePile(); } } if (TextUtils.isEmpty(conversationText)) { @@ -384,9 +412,10 @@ public class ConversationLayout extends FrameLayout && TextUtils.equals(conversationText, messageSender); messagingGroup.setCanHideSenderIfFirst(canHide); } + updateAppName(); updateIconPositionAndSize(); updateImageMessages(); - updateAppName(); + updatePaddingsBasedOnContentAvailability(); } private void updateImageMessages() { @@ -463,6 +492,38 @@ public class ConversationLayout extends FrameLayout secondLastIcon = createAvatarSymbol("", "", mLayoutColor); } topView.setImageIcon(secondLastIcon); + + int conversationAvatarSize; + int facepileAvatarSize; + int facePileBackgroundSize; + if (mIsCollapsed) { + conversationAvatarSize = mConversationAvatarSize; + facepileAvatarSize = mFacePileAvatarSize; + facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidth; + } else { + conversationAvatarSize = mConversationAvatarSizeExpanded; + facepileAvatarSize = mFacePileAvatarSizeExpandedGroup; + facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded; + } + LayoutParams layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + layoutParams.width = conversationAvatarSize; + layoutParams.height = conversationAvatarSize; + mConversationFacePile.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) bottomView.getLayoutParams(); + layoutParams.width = facepileAvatarSize; + layoutParams.height = facepileAvatarSize; + bottomView.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) topView.getLayoutParams(); + layoutParams.width = facepileAvatarSize; + layoutParams.height = facepileAvatarSize; + topView.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) bottomBackground.getLayoutParams(); + layoutParams.width = facePileBackgroundSize; + layoutParams.height = facePileBackgroundSize; + bottomBackground.setLayoutParams(layoutParams); } private void updateAppName() { @@ -477,30 +538,61 @@ public class ConversationLayout extends FrameLayout * update the icon position and sizing */ private void updateIconPositionAndSize() { - int gravity; - int marginStart; - int marginTop; - int iconSize; + int sidemargin; + int conversationAvatarSize; if (mIsOneToOne || mIsCollapsed) { - // Badged format - gravity = Gravity.LEFT; - marginStart = mBadgedSideMargins; - marginTop = mBadgedSideMargins; - iconSize = mIconSizeBadged; + sidemargin = mBadgedSideMargins; + conversationAvatarSize = mConversationAvatarSize; } else { - gravity = Gravity.CENTER_HORIZONTAL; - marginStart = 0; - marginTop = mExpandedGroupTopMargin; - iconSize = mIconSizeCentered; + sidemargin = mConversationFacePile.getVisibility() == VISIBLE + ? mExpandedGroupSideMarginFacePile + : mExpandedGroupSideMargin; + conversationAvatarSize = mConversationAvatarSizeExpanded; } LayoutParams layoutParams = (LayoutParams) mConversationIconBadge.getLayoutParams(); - layoutParams.gravity = gravity; - layoutParams.topMargin = marginTop; - layoutParams.setMarginStart(marginStart); - layoutParams.width = iconSize; - layoutParams.height = iconSize; + layoutParams.topMargin = sidemargin; + layoutParams.setMarginStart(sidemargin); mConversationIconBadge.setLayoutParams(layoutParams); + + if (mConversationIcon.getVisibility() == VISIBLE) { + layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + layoutParams.width = conversationAvatarSize; + layoutParams.height = conversationAvatarSize; + mConversationIcon.setLayoutParams(layoutParams); + } + } + + private void updatePaddingsBasedOnContentAvailability() { + int containerTopPadding; + int messagingPadding = 0; + if (mIsOneToOne || mIsCollapsed) { + containerTopPadding = mConversationIconTopPadding; + } else { + if (mAppName.getVisibility() != GONE) { + // The app name is visible, let's center outselves in the two lines + containerTopPadding = mConversationIconTopPaddingExpandedGroup; + } else { + // App name is gone, let's center ourselves int he one remaining line + containerTopPadding = mConversationIconTopPaddingNoAppName; + + // The app name is gone and we're a group, we'll need to add some extra padding + // to the messages, since otherwise it will overlap with the group + messagingPadding = mExpandedGroupMessagePaddingNoAppName; + } + } + + mConversationIconContainer.setPaddingRelative( + mConversationIconContainer.getPaddingStart(), + containerTopPadding, + mConversationIconContainer.getPaddingEnd(), + mConversationIconContainer.getPaddingBottom()); + + mMessagingLinearLayout.setPaddingRelative( + mMessagingLinearLayout.getPaddingStart(), + messagingPadding, + mMessagingLinearLayout.getPaddingEnd(), + mMessagingLinearLayout.getPaddingBottom()); } @RemotableViewMethod @@ -922,7 +1014,7 @@ public class ConversationLayout extends FrameLayout mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId)); } - private void updateContentPaddings() { + private void updateContentEndPaddings() { // Let's make sure the conversation header can't run into the expand button when we're // collapsed and update the paddings of the content @@ -951,6 +1043,14 @@ public class ConversationLayout extends FrameLayout mContentContainer.getPaddingBottom()); } + private void onAppNameVisibilityChanged() { + boolean appNameGone = mAppName.getVisibility() == GONE; + if (appNameGone != mAppNameGone) { + mAppNameGone = appNameGone; + updatePaddingsBasedOnContentAvailability(); + } + } + public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) { mExpandable = expandable; if (expandable) { @@ -960,7 +1060,7 @@ public class ConversationLayout extends FrameLayout // TODO: handle content paddings to end of layout mExpandButtonContainer.setVisibility(GONE); } - updateContentPaddings(); + updateContentEndPaddings(); } @Override diff --git a/core/java/com/android/internal/widget/ObservableTextView.java b/core/java/com/android/internal/widget/ObservableTextView.java new file mode 100644 index 0000000000000..1f3c296f0b60b --- /dev/null +++ b/core/java/com/android/internal/widget/ObservableTextView.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.widget; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RemoteViews; +import android.widget.TextView; + +import java.util.function.Consumer; + +/** + * A text view whose visibility can be observed. + */ +@RemoteViews.RemoteView +public class ObservableTextView extends TextView { + + private Consumer mOnVisibilityChangedListener; + + public ObservableTextView(Context context) { + super(context); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if (changedView == this) { + if (mOnVisibilityChangedListener != null) { + mOnVisibilityChangedListener.accept(visibility); + } + } + } + + public void setOnVisibilityChangedListener(Consumer listener) { + mOnVisibilityChangedListener = listener; + } +} diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml index 1db38702f9262..528562534aaba 100644 --- a/core/res/res/layout/conversation_face_pile_layout.xml +++ b/core/res/res/layout/conversation_face_pile_layout.xml @@ -23,21 +23,25 @@ > + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start|bottom"> + diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 7bf13ec4ad657..7cadecbbdb916 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -25,6 +25,7 @@ > - 36dp 20dp - - 26dp - - 12dp + + @dimen/messaging_avatar_size + + @dimen/messaging_avatar_size + + 25dp + + 22dp + + 18dp + + 2dp + + 1dp + + 14dp + + + 12dp + + + 9dp + + + 17.5dp 38dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 163501a5985e2..0adef7513bb5e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3874,15 +3874,25 @@ + - - - + + + + + + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java index 4882a235c5d8a..82f7c71c48cf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java @@ -116,11 +116,6 @@ public class ImageTransformState extends TransformState { return new ImageTransformState(); } - @Override - protected boolean transformScale(TransformState otherState) { - return sameAs(otherState); - } - @Override public void recycle() { super.recycle(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 82fb49144181e..27109d2acfa2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -248,7 +248,7 @@ public class TransformState { } protected boolean transformScale(TransformState otherState) { - return false; + return sameAs(otherState); } /** 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 91a2e7c815d47..593de23c58de4 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 @@ -53,6 +53,9 @@ class NotificationConversationTemplateViewWrapper constructor( private lateinit var conversationTitle: View private lateinit var importanceRing: View private lateinit var appName: View + private var facePileBottomBg: View? = null + private var facePileBottom: View? = null + private var facePileTop: View? = null private fun resolveViews() { messagingLinearLayout = conversationLayout.messagingLinearLayout @@ -67,6 +70,10 @@ class NotificationConversationTemplateViewWrapper constructor( importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring) appName = requireViewById(com.android.internal.R.id.app_name_text) conversationTitle = requireViewById(com.android.internal.R.id.conversation_text) + facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top) + facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom) + facePileBottomBg = + findViewById(com.android.internal.R.id.conversation_face_pile_bottom_background) } } @@ -118,7 +125,10 @@ class NotificationConversationTemplateViewWrapper constructor( conversationIcon, conversationBadgeBg, expandButton, - importanceRing + importanceRing, + facePileTop, + facePileBottom, + facePileBottomBg ) } @@ -140,9 +150,9 @@ class NotificationConversationTemplateViewWrapper constructor( else super.getMinLayoutHeight() - private fun addTransformedViews(vararg vs: View) = - vs.forEach(mTransformationHelper::addTransformedView) + private fun addTransformedViews(vararg vs: View?) = + vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) } - private fun addViewsTransformingToSimilar(vararg vs: View) = - vs.forEach(mTransformationHelper::addViewTransformingToSimilar) + private fun addViewsTransformingToSimilar(vararg vs: View?) = + vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) } }