Loading core/java/com/android/internal/widget/ConversationLayout.java +153 −51 Original line number Diff line number Diff line Loading @@ -93,7 +93,6 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mMessagingLinearLayout; private boolean mShowHistoricMessages; private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); private TextView mTitleView; private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; Loading @@ -108,8 +107,14 @@ 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 ImageView mConversationIconBadgeBg; private Icon mLargeIcon; private View mExpandButtonContainer; private ViewGroup mExpandButtonAndContentContainer; Loading @@ -117,11 +122,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; Loading @@ -133,7 +139,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); Loading Loading @@ -165,14 +176,15 @@ 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); 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. Loading @@ -192,18 +204,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 Loading Loading @@ -234,7 +268,7 @@ public class ConversationLayout extends FrameLayout mIsCollapsed = isCollapsed; mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); updateExpandButton(); updateContentPaddings(); updateContentEndPaddings(); } @RemotableViewMethod Loading Loading @@ -354,7 +388,6 @@ public class ConversationLayout extends FrameLayout } } } else { if (mIsCollapsed) { if (mLargeIcon != null) { mConversationIcon.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); Loading @@ -363,13 +396,10 @@ public class ConversationLayout extends FrameLayout 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(); } } else { mConversationFacePile.setVisibility(GONE); mConversationIcon.setVisibility(GONE); } } if (TextUtils.isEmpty(conversationText)) { conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName; Loading @@ -384,9 +414,10 @@ public class ConversationLayout extends FrameLayout && TextUtils.equals(conversationText, messageSender); messagingGroup.setCanHideSenderIfFirst(canHide); } updateAppName(); updateIconPositionAndSize(); updateImageMessages(); updateAppName(); updatePaddingsBasedOnContentAvailability(); } private void updateImageMessages() { Loading Loading @@ -425,7 +456,7 @@ public class ConversationLayout extends FrameLayout private void bindFacePile() { // Let's bind the face pile View bottomBackground = mConversationFacePile.findViewById( ImageView bottomBackground = mConversationFacePile.findViewById( R.id.conversation_face_pile_bottom_background); applyNotificationBackgroundColor(bottomBackground); ImageView bottomView = mConversationFacePile.findViewById( Loading Loading @@ -463,6 +494,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() { Loading @@ -477,30 +540,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 Loading Loading @@ -678,11 +772,11 @@ public class ConversationLayout extends FrameLayout @RemotableViewMethod public void setNotificationBackgroundColor(int color) { mNotificationBackgroundColor = color; applyNotificationBackgroundColor(mConversationIconBadge); applyNotificationBackgroundColor(mConversationIconBadgeBg); } private void applyNotificationBackgroundColor(View view) { view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); private void applyNotificationBackgroundColor(ImageView view) { view.setImageTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); } @RemotableViewMethod Loading Loading @@ -922,7 +1016,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 Loading Loading @@ -951,6 +1045,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) { Loading @@ -960,7 +1062,7 @@ public class ConversationLayout extends FrameLayout // TODO: handle content paddings to end of layout mExpandButtonContainer.setVisibility(GONE); } updateContentPaddings(); updateContentEndPaddings(); } @Override Loading core/java/com/android/internal/widget/ObservableTextView.java 0 → 100644 +66 −0 Original line number Diff line number Diff line /* * 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<Integer> 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<Integer> listener) { mOnVisibilityChangedListener = listener; } } core/res/res/layout/conversation_face_pile_layout.xml +13 −9 Original line number Diff line number Diff line Loading @@ -23,21 +23,25 @@ > <ImageView android:id="@+id/conversation_face_pile_top" android:layout_width="36dp" android:layout_height="36dp" android:layout_width="@dimen/messaging_avatar_size" android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="end|top" /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start|bottom"> <ImageView android:id="@+id/conversation_face_pile_bottom_background" android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="start|bottom" android:background="@drawable/conversation_badge_background"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/conversation_badge_background" /> <ImageView android:id="@+id/conversation_face_pile_bottom" android:layout_width="36dp" android:layout_height="36dp" android:layout_width="@dimen/messaging_avatar_size" android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="center" /> Loading core/res/res/layout/notification_template_material_conversation.xml +2 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ > <FrameLayout android:id="@+id/conversation_icon_container" android:layout_width="@dimen/conversation_content_start" android:layout_height="wrap_content" android:gravity="start|top" Loading Loading @@ -175,7 +176,7 @@ </LinearLayout> <!-- App Name --> <TextView <com.android.internal.widget.ObservableTextView android:id="@+id/app_name_text" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading core/res/res/values/dimens.xml +25 −4 Original line number Diff line number Diff line Loading @@ -696,10 +696,31 @@ <dimen name="conversation_badge_side_margin">36dp</dimen> <!-- size of the notification badge when applied to the conversation icon --> <dimen name="conversation_icon_size_badged">20dp</dimen> <!-- size of the notification badge when centered in a conversation --> <dimen name="conversation_icon_size_centered">26dp</dimen> <!-- margin on the top when the icon is centered for group conversations --> <dimen name="conversation_icon_margin_top_centered">12dp</dimen> <!-- size of the conversation avatar in an expanded group --> <dimen name="conversation_avatar_size_group_expanded">@dimen/messaging_avatar_size</dimen> <!-- size of the face pile icons --> <dimen name="conversation_face_pile_avatar_size">@dimen/messaging_avatar_size</dimen> <!-- size of the face pile icons when the group is expanded --> <dimen name="conversation_face_pile_avatar_size_group_expanded">25dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> <dimen name="conversation_badge_side_margin_group_expanded">22dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> <dimen name="conversation_badge_side_margin_group_expanded_face_pile">18dp</dimen> <!-- The width of the protection of the face pile layout--> <dimen name="conversation_face_pile_protection_width">2dp</dimen> <!-- The width of the protection of the face pile layout when expanded--> <dimen name="conversation_face_pile_protection_width_expanded">1dp</dimen> <!-- The padding of the expanded message container when the app name is gone--> <dimen name="expanded_group_conversation_message_padding_without_app_name">14dp</dimen> <!-- The top padding of the conversation icon container in the regular state--> <dimen name="conversation_icon_container_top_padding">12dp</dimen> <!-- The top padding of the conversation icon container when there's no app name present in a group--> <dimen name="conversation_icon_container_top_padding_no_app_name">9dp</dimen> <!-- The top padding of the conversation icon container when the avatar is small--> <dimen name="conversation_icon_container_top_padding_small_avatar">17.5dp</dimen> <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end --> <dimen name="conversation_header_expanded_padding_end">38dp</dimen> Loading Loading
core/java/com/android/internal/widget/ConversationLayout.java +153 −51 Original line number Diff line number Diff line Loading @@ -93,7 +93,6 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mMessagingLinearLayout; private boolean mShowHistoricMessages; private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); private TextView mTitleView; private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; Loading @@ -108,8 +107,14 @@ 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 ImageView mConversationIconBadgeBg; private Icon mLargeIcon; private View mExpandButtonContainer; private ViewGroup mExpandButtonAndContentContainer; Loading @@ -117,11 +122,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; Loading @@ -133,7 +139,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); Loading Loading @@ -165,14 +176,15 @@ 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); 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. Loading @@ -192,18 +204,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 Loading Loading @@ -234,7 +268,7 @@ public class ConversationLayout extends FrameLayout mIsCollapsed = isCollapsed; mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); updateExpandButton(); updateContentPaddings(); updateContentEndPaddings(); } @RemotableViewMethod Loading Loading @@ -354,7 +388,6 @@ public class ConversationLayout extends FrameLayout } } } else { if (mIsCollapsed) { if (mLargeIcon != null) { mConversationIcon.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); Loading @@ -363,13 +396,10 @@ public class ConversationLayout extends FrameLayout 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(); } } else { mConversationFacePile.setVisibility(GONE); mConversationIcon.setVisibility(GONE); } } if (TextUtils.isEmpty(conversationText)) { conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName; Loading @@ -384,9 +414,10 @@ public class ConversationLayout extends FrameLayout && TextUtils.equals(conversationText, messageSender); messagingGroup.setCanHideSenderIfFirst(canHide); } updateAppName(); updateIconPositionAndSize(); updateImageMessages(); updateAppName(); updatePaddingsBasedOnContentAvailability(); } private void updateImageMessages() { Loading Loading @@ -425,7 +456,7 @@ public class ConversationLayout extends FrameLayout private void bindFacePile() { // Let's bind the face pile View bottomBackground = mConversationFacePile.findViewById( ImageView bottomBackground = mConversationFacePile.findViewById( R.id.conversation_face_pile_bottom_background); applyNotificationBackgroundColor(bottomBackground); ImageView bottomView = mConversationFacePile.findViewById( Loading Loading @@ -463,6 +494,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() { Loading @@ -477,30 +540,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 Loading Loading @@ -678,11 +772,11 @@ public class ConversationLayout extends FrameLayout @RemotableViewMethod public void setNotificationBackgroundColor(int color) { mNotificationBackgroundColor = color; applyNotificationBackgroundColor(mConversationIconBadge); applyNotificationBackgroundColor(mConversationIconBadgeBg); } private void applyNotificationBackgroundColor(View view) { view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); private void applyNotificationBackgroundColor(ImageView view) { view.setImageTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); } @RemotableViewMethod Loading Loading @@ -922,7 +1016,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 Loading Loading @@ -951,6 +1045,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) { Loading @@ -960,7 +1062,7 @@ public class ConversationLayout extends FrameLayout // TODO: handle content paddings to end of layout mExpandButtonContainer.setVisibility(GONE); } updateContentPaddings(); updateContentEndPaddings(); } @Override Loading
core/java/com/android/internal/widget/ObservableTextView.java 0 → 100644 +66 −0 Original line number Diff line number Diff line /* * 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<Integer> 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<Integer> listener) { mOnVisibilityChangedListener = listener; } }
core/res/res/layout/conversation_face_pile_layout.xml +13 −9 Original line number Diff line number Diff line Loading @@ -23,21 +23,25 @@ > <ImageView android:id="@+id/conversation_face_pile_top" android:layout_width="36dp" android:layout_height="36dp" android:layout_width="@dimen/messaging_avatar_size" android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="end|top" /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start|bottom"> <ImageView android:id="@+id/conversation_face_pile_bottom_background" android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="start|bottom" android:background="@drawable/conversation_badge_background"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/conversation_badge_background" /> <ImageView android:id="@+id/conversation_face_pile_bottom" android:layout_width="36dp" android:layout_height="36dp" android:layout_width="@dimen/messaging_avatar_size" android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="center" /> Loading
core/res/res/layout/notification_template_material_conversation.xml +2 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ > <FrameLayout android:id="@+id/conversation_icon_container" android:layout_width="@dimen/conversation_content_start" android:layout_height="wrap_content" android:gravity="start|top" Loading Loading @@ -175,7 +176,7 @@ </LinearLayout> <!-- App Name --> <TextView <com.android.internal.widget.ObservableTextView android:id="@+id/app_name_text" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading
core/res/res/values/dimens.xml +25 −4 Original line number Diff line number Diff line Loading @@ -696,10 +696,31 @@ <dimen name="conversation_badge_side_margin">36dp</dimen> <!-- size of the notification badge when applied to the conversation icon --> <dimen name="conversation_icon_size_badged">20dp</dimen> <!-- size of the notification badge when centered in a conversation --> <dimen name="conversation_icon_size_centered">26dp</dimen> <!-- margin on the top when the icon is centered for group conversations --> <dimen name="conversation_icon_margin_top_centered">12dp</dimen> <!-- size of the conversation avatar in an expanded group --> <dimen name="conversation_avatar_size_group_expanded">@dimen/messaging_avatar_size</dimen> <!-- size of the face pile icons --> <dimen name="conversation_face_pile_avatar_size">@dimen/messaging_avatar_size</dimen> <!-- size of the face pile icons when the group is expanded --> <dimen name="conversation_face_pile_avatar_size_group_expanded">25dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> <dimen name="conversation_badge_side_margin_group_expanded">22dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> <dimen name="conversation_badge_side_margin_group_expanded_face_pile">18dp</dimen> <!-- The width of the protection of the face pile layout--> <dimen name="conversation_face_pile_protection_width">2dp</dimen> <!-- The width of the protection of the face pile layout when expanded--> <dimen name="conversation_face_pile_protection_width_expanded">1dp</dimen> <!-- The padding of the expanded message container when the app name is gone--> <dimen name="expanded_group_conversation_message_padding_without_app_name">14dp</dimen> <!-- The top padding of the conversation icon container in the regular state--> <dimen name="conversation_icon_container_top_padding">12dp</dimen> <!-- The top padding of the conversation icon container when there's no app name present in a group--> <dimen name="conversation_icon_container_top_padding_no_app_name">9dp</dimen> <!-- The top padding of the conversation icon container when the avatar is small--> <dimen name="conversation_icon_container_top_padding_small_avatar">17.5dp</dimen> <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end --> <dimen name="conversation_header_expanded_padding_end">38dp</dimen> Loading