diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 26684201019c4..297ebb0ad94d7 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -35,10 +35,14 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.Typeface; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcelable; +import android.text.Spannable; +import android.text.SpannableString; import android.text.TextUtils; +import android.text.style.StyleSpan; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -109,7 +113,7 @@ public class ConversationLayout extends FrameLayout private CharSequence mNameReplacement; private boolean mIsCollapsed; private ImageResolver mImageResolver; - private CachingIconView mConversationIcon; + private CachingIconView mConversationIconView; private View mConversationIconContainer; private int mConversationIconTopPadding; private int mConversationIconTopPaddingExpandedGroup; @@ -157,6 +161,7 @@ public class ConversationLayout extends FrameLayout private ViewGroup mAppOps; private Rect mAppOpsTouchRect = new Rect(); private float mMinTouchSize; + private Icon mConversationIcon; public ConversationLayout(@NonNull Context context) { super(context); @@ -192,7 +197,7 @@ public class ConversationLayout extends FrameLayout mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setAntiAlias(true); - mConversationIcon = findViewById(R.id.conversation_icon); + mConversationIconView = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); mAppOps = findViewById(com.android.internal.R.id.app_ops); @@ -232,7 +237,7 @@ public class ConversationLayout extends FrameLayout }); // When the conversation icon is gone, hide the whole badge - mConversationIcon.setOnForceHiddenChangedListener((forceHidden) -> { + mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> { animateViewForceHidden(mConversationIconBadgeBg, forceHidden); animateViewForceHidden(mImportanceRingView, forceHidden); animateViewForceHidden(mIcon, forceHidden); @@ -463,7 +468,7 @@ public class ConversationLayout extends FrameLayout CharSequence conversationText = mConversationTitle; if (mIsOneToOne) { // Let's resolve the icon / text from the last sender - mConversationIcon.setVisibility(VISIBLE); + mConversationIconView.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); CharSequence userKey = getKey(mUser); for (int i = mGroups.size() - 1; i >= 0; i--) { @@ -480,17 +485,20 @@ public class ConversationLayout extends FrameLayout if (avatarIcon == null) { avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor); } - mConversationIcon.setImageIcon(avatarIcon); + mConversationIcon = avatarIcon; + mConversationIconView.setImageIcon(mConversationIcon); break; } } } else { if (mLargeIcon != null) { - mConversationIcon.setVisibility(VISIBLE); + mConversationIcon = mLargeIcon; + mConversationIconView.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); - mConversationIcon.setImageIcon(mLargeIcon); + mConversationIconView.setImageIcon(mLargeIcon); } else { - mConversationIcon.setVisibility(GONE); + mConversationIcon = null; + mConversationIconView.setVisibility(GONE); // This will also inflate it! mConversationFacePile.setVisibility(VISIBLE); // rebind the value to the inflated view instead of the stub @@ -561,15 +569,8 @@ public class ConversationLayout extends FrameLayout mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE); } - private void bindFacePile() { - // Let's bind the face pile - ImageView bottomBackground = mConversationFacePile.findViewById( - R.id.conversation_face_pile_bottom_background); + public void bindFacePile(ImageView bottomBackground, ImageView bottomView, ImageView topView) { applyNotificationBackgroundColor(bottomBackground); - ImageView bottomView = mConversationFacePile.findViewById( - R.id.conversation_face_pile_bottom); - ImageView topView = mConversationFacePile.findViewById( - R.id.conversation_face_pile_top); // Let's find the two last conversations: Icon secondLastIcon = null; CharSequence lastKey = null; @@ -601,6 +602,17 @@ public class ConversationLayout extends FrameLayout secondLastIcon = createAvatarSymbol("", "", mLayoutColor); } topView.setImageIcon(secondLastIcon); + } + + private void bindFacePile() { + ImageView bottomBackground = mConversationFacePile.findViewById( + R.id.conversation_face_pile_bottom_background); + ImageView bottomView = mConversationFacePile.findViewById( + R.id.conversation_face_pile_bottom); + ImageView topView = mConversationFacePile.findViewById( + R.id.conversation_face_pile_top); + + bindFacePile(bottomBackground, bottomView, topView); int conversationAvatarSize; int facepileAvatarSize; @@ -614,7 +626,7 @@ public class ConversationLayout extends FrameLayout facepileAvatarSize = mFacePileAvatarSizeExpandedGroup; facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded; } - LayoutParams layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + LayoutParams layoutParams = (LayoutParams) mConversationIconView.getLayoutParams(); layoutParams.width = conversationAvatarSize; layoutParams.height = conversationAvatarSize; mConversationFacePile.setLayoutParams(layoutParams); @@ -664,11 +676,11 @@ public class ConversationLayout extends FrameLayout layoutParams.setMarginStart(sidemargin); mConversationIconBadge.setLayoutParams(layoutParams); - if (mConversationIcon.getVisibility() == VISIBLE) { - layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + if (mConversationIconView.getVisibility() == VISIBLE) { + layoutParams = (LayoutParams) mConversationIconView.getLayoutParams(); layoutParams.width = conversationAvatarSize; layoutParams.height = conversationAvatarSize; - mConversationIcon.setLayoutParams(layoutParams); + mConversationIconView.setLayoutParams(layoutParams); } } @@ -719,6 +731,10 @@ public class ConversationLayout extends FrameLayout mConversationTitle = conversationTitle; } + public CharSequence getConversationTitle() { + return mConversationText.getText(); + } + private void removeGroups(ArrayList oldGroups) { int size = oldGroups.size(); for (int i = 0; i < size; i++) { @@ -1218,4 +1234,43 @@ public class ConversationLayout extends FrameLayout public void setMessagingClippingDisabled(boolean clippingDisabled) { mMessagingLinearLayout.setClipBounds(clippingDisabled ? null : mMessagingClipRect); } + + @Nullable + public CharSequence getConversationSenderName() { + if (mGroups.isEmpty()) { + return null; + } + final CharSequence name = mGroups.get(mGroups.size() - 1).getSenderName(); + return getResources().getString(R.string.conversation_single_line_name_display, name); + } + + public boolean isOneToOne() { + return mIsOneToOne; + } + + @Nullable + public CharSequence getConversationText() { + if (mMessages.isEmpty()) { + return null; + } + final MessagingMessage messagingMessage = mMessages.get(mMessages.size() - 1); + final CharSequence text = messagingMessage.getMessage().getText(); + if (text == null && messagingMessage instanceof MessagingImageMessage) { + final String unformatted = + getResources().getString(R.string.conversation_single_line_image_placeholder); + SpannableString spannableString = new SpannableString(unformatted); + spannableString.setSpan( + new StyleSpan(Typeface.ITALIC), + 0, + spannableString.length(), + Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + return spannableString; + } + return text; + } + + @Nullable + public Icon getConversationIcon() { + return mConversationIcon; + } } diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml index 528562534aaba..95c14d7c6b5a5 100644 --- a/core/res/res/layout/conversation_face_pile_layout.xml +++ b/core/res/res/layout/conversation_face_pile_layout.xml @@ -12,7 +12,7 @@ ~ 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 + ~ limitations underthe License --> - + + + + %1$s: + + sent an image + Conversation diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 64768cf4c730d..e9ac679ec39c9 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -296,6 +296,10 @@ easier. + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4efd7ff3207eb..8f7cc8245e442 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1599,6 +1599,8 @@ + + @@ -3863,7 +3865,10 @@ + + + @@ -3907,6 +3912,7 @@ + diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index f4857932064f5..eb02a1c8b880a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -279,8 +279,12 @@ public class Utils { } public static int getThemeAttr(Context context, int attr) { + return getThemeAttr(context, attr, 0); + } + + public static int getThemeAttr(Context context, int attr, int defaultValue) { TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); - int theme = ta.getResourceId(0, 0); + int theme = ta.getResourceId(0, defaultValue); ta.recycle(); return theme; } diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml new file mode 100644 index 0000000000000..e6f27904ee582 --- /dev/null +++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 179f8b8ea9f4d..e2d7269d9ff52 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -625,6 +625,15 @@ @dimen/notification_side_paddings + + 36dp + + + 24dp + + + 1dp + 36dp diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4ed819e4925ba..c6779f7799724 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -475,7 +475,6 @@ ?android:attr/textColorTertiary - diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index c613e64438820..b4d0bf8f8cce5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -536,6 +536,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return isNonblockable; } + private boolean isConversation() { + return mPeopleNotificationIdentifier + .getPeopleNotificationType(mEntry.getSbn(), mEntry.getRanking()) + != PeopleNotificationIdentifier.TYPE_NON_PERSON; + } + public void onNotificationUpdated() { for (NotificationContentView l : mLayouts) { l.onNotificationUpdated(mEntry); @@ -548,7 +554,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mMenuRow.setAppName(mAppName); } if (mIsSummaryWithChildren) { - mChildrenContainer.recreateNotificationHeader(mExpandClickListener); + mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation()); mChildrenContainer.onNotificationUpdated(); } if (mIconAnimationRunning) { @@ -2358,8 +2364,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mIsSummaryWithChildren = mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0; if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) { - mChildrenContainer.recreateNotificationHeader(mExpandClickListener - ); + mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation()); } getShowingLayout().updateBackgroundColor(false /* animate */); mPrivateLayout.updateExpandButtons(isExpandable()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java new file mode 100644 index 0000000000000..32477a6f39b7b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java @@ -0,0 +1,136 @@ +/* + * 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.systemui.statusbar.notification.row; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.widget.ConversationLayout; +import com.android.systemui.R; + +/** + * A hybrid view which may contain information about one ore more conversations. + */ +public class HybridConversationNotificationView extends HybridNotificationView { + + private ImageView mConversationIconView; + private TextView mConversationSenderName; + private View mConversationFacePile; + private int mConversationIconSize; + private int mFacePileSize; + private int mFacePileProtectionWidth; + + public HybridConversationNotificationView(Context context) { + this(context, null); + } + + public HybridConversationNotificationView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public HybridConversationNotificationView( + Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public HybridConversationNotificationView( + Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mConversationIconView = requireViewById(com.android.internal.R.id.conversation_icon); + mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile); + mConversationSenderName = requireViewById(R.id.conversation_notification_sender); + mFacePileSize = getResources() + .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size); + mConversationIconSize = getResources() + .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size); + mFacePileProtectionWidth = getResources().getDimensionPixelSize( + R.dimen.conversation_single_line_face_pile_protection_width); + mTransformationHelper.addViewTransformingToSimilar(mConversationIconView); + } + + @Override + public void bind(@Nullable CharSequence title, @Nullable CharSequence text, + @Nullable View contentView) { + if (!(contentView instanceof ConversationLayout)) { + super.bind(title, text, contentView); + return; + } + + ConversationLayout conversationLayout = (ConversationLayout) contentView; + Icon conversationIcon = conversationLayout.getConversationIcon(); + if (conversationIcon != null) { + mConversationFacePile.setVisibility(GONE); + mConversationIconView.setVisibility(VISIBLE); + mConversationIconView.setImageIcon(conversationIcon); + } else { + // If there isn't an icon, generate a "face pile" based on the sender avatars + mConversationIconView.setVisibility(GONE); + mConversationFacePile.setVisibility(VISIBLE); + + mConversationFacePile = + requireViewById(com.android.internal.R.id.conversation_face_pile); + ImageView facePileBottomBg = mConversationFacePile.requireViewById( + com.android.internal.R.id.conversation_face_pile_bottom_background); + ImageView facePileBottom = mConversationFacePile.requireViewById( + com.android.internal.R.id.conversation_face_pile_bottom); + ImageView facePileTop = mConversationFacePile.requireViewById( + com.android.internal.R.id.conversation_face_pile_top); + conversationLayout.bindFacePile(facePileBottomBg, facePileBottom, facePileTop); + setSize(mConversationFacePile, mFacePileSize); + setSize(facePileBottom, mConversationIconSize); + setSize(facePileTop, mConversationIconSize); + setSize(facePileBottomBg, mConversationIconSize + 2 * mFacePileProtectionWidth); + mTransformationHelper.addViewTransformingToSimilar(facePileTop); + mTransformationHelper.addViewTransformingToSimilar(facePileBottom); + mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg); + } + CharSequence conversationTitle = conversationLayout.getConversationTitle(); + if (TextUtils.isEmpty(conversationTitle)) { + conversationTitle = title; + } + if (conversationLayout.isOneToOne()) { + mConversationSenderName.setVisibility(GONE); + } else { + mConversationSenderName.setVisibility(VISIBLE); + mConversationSenderName.setText(conversationLayout.getConversationSenderName()); + } + CharSequence conversationText = conversationLayout.getConversationText(); + if (TextUtils.isEmpty(conversationText)) { + conversationText = text; + } + super.bind(conversationTitle, conversationText, conversationLayout); + } + + private static void setSize(View view, int size) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); + lp.width = size; + lp.height = size; + view.setLayoutParams(lp); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index fe819574f3b62..0ccebc130b1d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -16,18 +16,20 @@ package com.android.systemui.statusbar.notification.row; +import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.content.res.Resources; +import android.service.notification.StatusBarNotification; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.android.internal.widget.ConversationLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationDozeHelper; -import com.android.systemui.statusbar.notification.NotificationUtils; /** * A class managing hybrid groups that include {@link HybridNotificationView} and the notification @@ -36,41 +38,41 @@ import com.android.systemui.statusbar.notification.NotificationUtils; public class HybridGroupManager { private final Context mContext; - private final ViewGroup mParent; private float mOverflowNumberSize; private int mOverflowNumberPadding; private int mOverflowNumberColor; - public HybridGroupManager(Context ctx, ViewGroup parent) { + public HybridGroupManager(Context ctx) { mContext = ctx; - mParent = parent; initDimens(); } public void initDimens() { Resources res = mContext.getResources(); - mOverflowNumberSize = res.getDimensionPixelSize( - R.dimen.group_overflow_number_size); - mOverflowNumberPadding = res.getDimensionPixelSize( - R.dimen.group_overflow_number_padding); + mOverflowNumberSize = res.getDimensionPixelSize(R.dimen.group_overflow_number_size); + mOverflowNumberPadding = res.getDimensionPixelSize(R.dimen.group_overflow_number_padding); } - private HybridNotificationView inflateHybridViewWithStyle(int style) { + private HybridNotificationView inflateHybridViewWithStyle(int style, + View contentView, ViewGroup parent) { LayoutInflater inflater = new ContextThemeWrapper(mContext, style) .getSystemService(LayoutInflater.class); - HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate( - R.layout.hybrid_notification, mParent, false); - mParent.addView(hybrid); + int layout = contentView instanceof ConversationLayout + ? R.layout.hybrid_conversation_notification + : R.layout.hybrid_notification; + HybridNotificationView hybrid = (HybridNotificationView) + inflater.inflate(layout, parent, false); + parent.addView(hybrid); return hybrid; } - private TextView inflateOverflowNumber() { + private TextView inflateOverflowNumber(ViewGroup parent) { LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class); TextView numberView = (TextView) inflater.inflate( - R.layout.hybrid_overflow_number, mParent, false); - mParent.addView(numberView); + R.layout.hybrid_overflow_number, parent, false); + parent.addView(numberView); updateOverFlowNumberColor(numberView); return numberView; } @@ -87,22 +89,26 @@ public class HybridGroupManager { } public HybridNotificationView bindFromNotification(HybridNotificationView reusableView, - Notification notification) { - return bindFromNotificationWithStyle(reusableView, notification, - R.style.HybridNotification); + View contentView, StatusBarNotification notification, + ViewGroup parent) { + return bindFromNotificationWithStyle(reusableView, contentView, notification, + R.style.HybridNotification, parent); } private HybridNotificationView bindFromNotificationWithStyle( - HybridNotificationView reusableView, Notification notification, int style) { + HybridNotificationView reusableView, View contentView, + StatusBarNotification notification, + int style, ViewGroup parent) { if (reusableView == null) { - reusableView = inflateHybridViewWithStyle(style); + reusableView = inflateHybridViewWithStyle(style, contentView, parent); } - CharSequence titleText = resolveTitle(notification); - CharSequence contentText = resolveText(notification); - reusableView.bind(titleText, contentText); + CharSequence titleText = resolveTitle(notification.getNotification()); + CharSequence contentText = resolveText(notification.getNotification()); + reusableView.bind(titleText, contentText, contentView); return reusableView; } + @Nullable private CharSequence resolveText(Notification notification) { CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT); if (contentText == null) { @@ -111,6 +117,7 @@ public class HybridGroupManager { return contentText; } + @Nullable private CharSequence resolveTitle(Notification notification) { CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE); if (titleText == null) { @@ -119,9 +126,10 @@ public class HybridGroupManager { return titleText; } - public TextView bindOverflowNumber(TextView reusableView, int number) { + public TextView bindOverflowNumber(TextView reusableView, int number, + ViewGroup parent) { if (reusableView == null) { - reusableView = inflateOverflowNumber(); + reusableView = inflateOverflowNumber(parent); } String text = mContext.getResources().getString( R.string.notification_group_overflow_indicator, number); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java index be25d63779128..207144931c3ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java @@ -36,8 +36,7 @@ import com.android.systemui.statusbar.notification.TransformState; public class HybridNotificationView extends AlphaOptimizedLinearLayout implements TransformableView { - private ViewTransformationHelper mTransformationHelper; - + protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper(); protected TextView mTitleView; protected TextView mTextView; @@ -69,9 +68,8 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout @Override protected void onFinishInflate() { super.onFinishInflate(); - mTitleView = (TextView) findViewById(R.id.notification_title); - mTextView = (TextView) findViewById(R.id.notification_text); - mTransformationHelper = new ViewTransformationHelper(); + mTitleView = findViewById(R.id.notification_title); + mTextView = findViewById(R.id.notification_text); mTransformationHelper.setCustomTransformation( new ViewTransformationHelper.CustomTransformation() { @Override @@ -106,11 +104,8 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView); } - public void bind(CharSequence title) { - bind(title, null); - } - - public void bind(CharSequence title, CharSequence text) { + public void bind(@Nullable CharSequence title, @Nullable CharSequence text, + @Nullable View contentView) { mTitleView.setText(title); mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE); if (TextUtils.isEmpty(text)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index bd1745eaa0284..f23f3bf283125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -177,7 +177,7 @@ public class NotificationContentView extends FrameLayout { public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); - mHybridGroupManager = new HybridGroupManager(getContext(), this); + mHybridGroupManager = new HybridGroupManager(getContext()); mMediaTransferManager = new MediaTransferManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); @@ -1163,7 +1163,7 @@ public class NotificationContentView extends FrameLayout { if (mIsChildInGroup) { boolean isNewView = mSingleLineView == null; mSingleLineView = mHybridGroupManager.bindFromNotification( - mSingleLineView, mStatusBarNotification.getNotification()); + mSingleLineView, mContractedChild, mStatusBarNotification, this); if (isNewView) { updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView); 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 13e7fe5cd6d80..15499b87d56de 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 @@ -46,13 +46,13 @@ class NotificationConversationTemplateViewWrapper constructor( ) private val conversationLayout: ConversationLayout = view as ConversationLayout - private lateinit var conversationIcon: CachingIconView + private lateinit var conversationIconView: CachingIconView private lateinit var conversationBadgeBg: View private lateinit var expandButton: View private lateinit var expandButtonContainer: View private lateinit var imageMessageContainer: ViewGroup private lateinit var messagingLinearLayout: MessagingLinearLayout - private lateinit var conversationTitle: View + private lateinit var conversationTitleView: View private lateinit var importanceRing: View private lateinit var appName: View private var facePileBottomBg: View? = null @@ -63,7 +63,7 @@ class NotificationConversationTemplateViewWrapper constructor( messagingLinearLayout = conversationLayout.messagingLinearLayout imageMessageContainer = conversationLayout.imageMessageContainer with(conversationLayout) { - conversationIcon = requireViewById(com.android.internal.R.id.conversation_icon) + conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon) conversationBadgeBg = requireViewById(com.android.internal.R.id.conversation_icon_badge_bg) expandButton = requireViewById(com.android.internal.R.id.expand_button) @@ -71,7 +71,7 @@ class NotificationConversationTemplateViewWrapper constructor( requireViewById(com.android.internal.R.id.expand_button_container) 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) + conversationTitleView = 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 = @@ -93,7 +93,7 @@ class NotificationConversationTemplateViewWrapper constructor( addTransformedViews( messagingLinearLayout, appName, - conversationTitle) + conversationTitleView) // Let's ignore the image message container since that is transforming as part of the // messages already @@ -124,7 +124,7 @@ class NotificationConversationTemplateViewWrapper constructor( ) addViewsTransformingToSimilar( - conversationIcon, + conversationIconView, conversationBadgeBg, expandButton, importanceRing, @@ -136,29 +136,27 @@ class NotificationConversationTemplateViewWrapper constructor( override fun setShelfIconVisible(visible: Boolean) { if (conversationLayout.isImportantConversation) { - if (conversationIcon.visibility != GONE) { - conversationIcon.setForceHidden(visible); + if (conversationIconView.visibility != GONE) { + conversationIconView.isForceHidden = 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; + 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 getShelfTransformationTarget(): View? = + if (conversationLayout.isImportantConversation) + if (conversationIconView.visibility != GONE) + conversationIconView + 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. + super.getShelfTransformationTarget() + else + super.getShelfTransformationTarget() override fun setRemoteInputVisible(visible: Boolean) = conversationLayout.showHistoricMessages(visible) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index e20be2b719396..f8b783113ccbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -27,11 +27,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.NotificationExpandButton; +import com.android.settingslib.Utils; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.TransformableView; @@ -60,12 +62,14 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private NotificationExpandButton mExpandButton; protected NotificationHeaderView mNotificationHeader; private TextView mHeaderText; + private TextView mAppNameText; private ImageView mWorkProfileImage; private View mCameraIcon; private View mMicIcon; private View mOverlayIcon; private View mAppOps; private View mAudiblyAlertedIcon; + private FrameLayout mIconContainer; private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; @@ -108,8 +112,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } protected void resolveHeaderViews() { + mIconContainer = mView.findViewById(com.android.internal.R.id.header_icon_container); mIcon = mView.findViewById(com.android.internal.R.id.icon); mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); + mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); @@ -182,6 +188,61 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } } + public void applyConversationSkin() { + if (mAppNameText != null) { + mAppNameText.setTextAppearance( + com.android.internal.R.style + .TextAppearance_DeviceDefault_Notification_Conversation_AppName); + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams(); + layoutParams.setMarginStart(0); + } + if (mIconContainer != null) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams(); + layoutParams.width = + mIconContainer.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.conversation_content_start); + final int marginStart = + mIconContainer.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_start); + layoutParams.setMarginStart(marginStart * -1); + } + if (mIcon != null) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams(); + layoutParams.setMarginEnd(0); + } + } + + public void clearConversationSkin() { + if (mAppNameText != null) { + final int textAppearance = Utils.getThemeAttr( + mAppNameText.getContext(), + com.android.internal.R.attr.notificationHeaderTextAppearance, + com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info); + mAppNameText.setTextAppearance(textAppearance); + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams(); + final int marginStart = mAppNameText.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_header_app_name_margin_start); + layoutParams.setMarginStart(marginStart); + } + if (mIconContainer != null) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; + layoutParams.setMarginStart(0); + } + if (mIcon != null) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams(); + final int marginEnd = mIcon.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_header_icon_margin_end); + layoutParams.setMarginEnd(marginEnd); + } + } + /** * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each * child is faded automatically and doesn't have to be manually added. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 351a3ef46decc..c9b1318feb13b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -40,6 +40,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.HybridGroupManager; import com.android.systemui.statusbar.notification.row.HybridNotificationView; +import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import java.util.ArrayList; @@ -99,6 +100,7 @@ public class NotificationChildrenContainer extends ViewGroup { private boolean mIsLowPriority; private OnClickListener mHeaderClickListener; private ViewGroup mCurrentHeader; + private boolean mIsConversation; private boolean mShowDividersWhenExpanded; private boolean mHideDividersDuringExpand; @@ -122,7 +124,7 @@ public class NotificationChildrenContainer extends ViewGroup { public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mHybridGroupManager = new HybridGroupManager(getContext(), this); + mHybridGroupManager = new HybridGroupManager(getContext()); initDimens(); setClipChildren(false); } @@ -308,8 +310,9 @@ public class NotificationChildrenContainer extends ViewGroup { return mAttachedChildren.size(); } - public void recreateNotificationHeader(OnClickListener listener) { + public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) { mHeaderClickListener = listener; + mIsConversation = isConversation; StatusBarNotification notification = mContainingNotification.getEntry().getSbn(); final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), notification.getNotification()); @@ -328,7 +331,16 @@ public class NotificationChildrenContainer extends ViewGroup { header.reapply(getContext(), mNotificationHeader); } mNotificationHeaderWrapper.onContentUpdated(mContainingNotification); - recreateLowPriorityHeader(builder); + if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) { + NotificationHeaderViewWrapper headerWrapper = + (NotificationHeaderViewWrapper) mNotificationHeaderWrapper; + if (isConversation) { + headerWrapper.applyConversationSkin(); + } else { + headerWrapper.clearConversationSkin(); + } + } + recreateLowPriorityHeader(builder, isConversation); updateHeaderVisibility(false /* animate */); updateChildrenHeaderAppearance(); } @@ -338,7 +350,7 @@ public class NotificationChildrenContainer extends ViewGroup { * * @param builder a builder to reuse. Otherwise the builder will be recovered. */ - private void recreateLowPriorityHeader(Notification.Builder builder) { + private void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) { RemoteViews header; StatusBarNotification notification = mContainingNotification.getEntry().getSbn(); if (mIsLowPriority) { @@ -362,6 +374,15 @@ public class NotificationChildrenContainer extends ViewGroup { header.reapply(getContext(), mNotificationHeaderLowPriority); } mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification); + if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) { + NotificationHeaderViewWrapper headerWrapper = + (NotificationHeaderViewWrapper) mNotificationHeaderWrapper; + if (isConversation) { + headerWrapper.applyConversationSkin(); + } else { + headerWrapper.clearConversationSkin(); + } + } resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader()); } else { removeView(mNotificationHeaderLowPriority); @@ -378,7 +399,7 @@ public class NotificationChildrenContainer extends ViewGroup { int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */); if (mUntruncatedChildCount > maxAllowedVisibleChildren) { int number = mUntruncatedChildCount - maxAllowedVisibleChildren; - mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number); + mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number, this); if (mGroupOverFlowState == null) { mGroupOverFlowState = new ViewState(); mNeverAppliedGroupState = true; @@ -1141,7 +1162,7 @@ public class NotificationChildrenContainer extends ViewGroup { removeView(mNotificationHeaderLowPriority); mNotificationHeaderLowPriority = null; } - recreateNotificationHeader(listener); + recreateNotificationHeader(listener, mIsConversation); initDimens(); for (int i = 0; i < mDividers.size(); i++) { View prevDivider = mDividers.get(i); @@ -1225,7 +1246,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void setIsLowPriority(boolean isLowPriority) { mIsLowPriority = isLowPriority; if (mContainingNotification != null) { /* we're not yet set up yet otherwise */ - recreateLowPriorityHeader(null /* existingBuilder */); + recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation); updateHeaderVisibility(false /* animate */); } if (mUserLocked) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index 703789151895c..b9055ec0bb7bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -146,7 +146,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { @Test public void testRecreateNotificationHeader_hasHeader() { - mChildrenContainer.recreateNotificationHeader(null); + mChildrenContainer.recreateNotificationHeader(null, false); Assert.assertNotNull("Children container must have a header after recreation", mChildrenContainer.getCurrentHeaderView()); }