Loading core/java/com/android/internal/widget/ConversationAvatarData.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,9 +21,9 @@ import android.graphics.drawable.Drawable; /** * @hide */ interface ConversationAvatarData { public interface ConversationAvatarData { final class OneToOneConversationAvatarData implements ConversationAvatarData { final Drawable mDrawable; public final Drawable mDrawable; OneToOneConversationAvatarData(Drawable drawable) { mDrawable = drawable; Loading core/java/com/android/internal/widget/ConversationHeaderData.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.annotation.Nullable; /** * @hide */ final class ConversationHeaderData { public final class ConversationHeaderData { private final CharSequence mConversationText; private final ConversationAvatarData mConversationAvatarData; Loading @@ -38,7 +38,7 @@ final class ConversationHeaderData { } @Nullable ConversationAvatarData getConversationAvatar() { public ConversationAvatarData getConversationAvatar() { return mConversationAvatarData; } } core/java/com/android/internal/widget/ConversationLayout.java +12 −1 Original line number Diff line number Diff line Loading @@ -162,6 +162,8 @@ public class ConversationLayout extends FrameLayout private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this); private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>(); private boolean mPrecomputedTextEnabled = false; @Nullable private ConversationHeaderData mConversationHeaderData; public ConversationLayout(@NonNull Context context) { super(context); Loading Loading @@ -651,6 +653,7 @@ public class ConversationLayout extends FrameLayout private void setConversationAvatarAndNameFromData( ConversationHeaderData conversationHeaderData) { mConversationHeaderData = conversationHeaderData; final OneToOneConversationAvatarData oneToOneConversationDrawable; final GroupConversationAvatarData groupConversationAvatarData; final ConversationAvatarData conversationAvatar = Loading Loading @@ -804,7 +807,10 @@ public class ConversationLayout extends FrameLayout bottomBackground.setLayoutParams(layoutParams); } private void bindFacePileWithDrawable(ImageView bottomBackground, ImageView bottomView, /** * Binds group avatar drawables to face pile. */ public void bindFacePileWithDrawable(ImageView bottomBackground, ImageView bottomView, ImageView topView, GroupConversationAvatarData groupConversationAvatarData) { applyNotificationBackgroundColor(bottomBackground); bottomView.setImageDrawable(groupConversationAvatarData.mLastIcon); Loading Loading @@ -1573,6 +1579,11 @@ public class ConversationLayout extends FrameLayout return mConversationIcon; } @Nullable public ConversationHeaderData getConversationHeaderData() { return mConversationHeaderData; } private static class TouchDelegateComposite extends TouchDelegate { private final ArrayList<TouchDelegate> mDelegates = new ArrayList<>(); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java +82 −18 Original line number Diff line number Diff line Loading @@ -30,14 +30,21 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ConversationAvatarData; import com.android.internal.widget.ConversationAvatarData.GroupConversationAvatarData; import com.android.internal.widget.ConversationAvatarData.OneToOneConversationAvatarData; import com.android.internal.widget.ConversationHeaderData; import com.android.internal.widget.ConversationLayout; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.shared.ConversationStyleSetAvatarAsync; import com.android.systemui.statusbar.notification.row.ui.viewmodel.ConversationAvatar; import com.android.systemui.statusbar.notification.row.ui.viewmodel.FacePile; import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleIcon; import java.util.Objects; /** * A hybrid view which may contain information about one ore more conversations. */ Loading Loading @@ -111,7 +118,38 @@ public class HybridConversationNotificationView extends HybridNotificationView { } ConversationLayout conversationLayout = (ConversationLayout) contentView; Icon conversationIcon = conversationLayout.getConversationIcon(); loadConversationAvatar(conversationLayout); 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 void loadConversationAvatar(ConversationLayout conversationLayout) { AsyncHybridViewInflation.assertInLegacyMode(); if (ConversationStyleSetAvatarAsync.isEnabled()) { loadConversationAvatarWithDrawable(conversationLayout); } else { loadConversationAvatarWithIcon(conversationLayout); } } @Deprecated private void loadConversationAvatarWithIcon(ConversationLayout conversationLayout) { ConversationStyleSetAvatarAsync.assertInLegacyMode(); AsyncHybridViewInflation.assertInLegacyMode(); final Icon conversationIcon = conversationLayout.getConversationIcon(); if (conversationIcon != null) { mConversationFacePile.setVisibility(GONE); mConversationIconView.setVisibility(VISIBLE); Loading @@ -124,11 +162,11 @@ public class HybridConversationNotificationView extends HybridNotificationView { mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile); ImageView facePileBottomBg = mConversationFacePile.requireViewById( final ImageView facePileBottomBg = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom_background); ImageView facePileBottom = mConversationFacePile.requireViewById( final ImageView facePileBottom = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom); ImageView facePileTop = mConversationFacePile.requireViewById( final ImageView facePileTop = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_top); conversationLayout.bindFacePile(facePileBottomBg, facePileBottom, facePileTop); setSize(mConversationFacePile, mFacePileSize); Loading @@ -139,21 +177,47 @@ public class HybridConversationNotificationView extends HybridNotificationView { mTransformationHelper.addViewTransformingToSimilar(facePileBottom); mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg); } CharSequence conversationTitle = conversationLayout.getConversationTitle(); if (TextUtils.isEmpty(conversationTitle)) { conversationTitle = title; } if (conversationLayout.isOneToOne()) { mConversationSenderName.setVisibility(GONE); private void loadConversationAvatarWithDrawable(ConversationLayout conversationLayout) { AsyncHybridViewInflation.assertInLegacyMode(); final ConversationHeaderData conversationHeaderData = Objects.requireNonNull( conversationLayout.getConversationHeaderData(), /* message = */ "conversationHeaderData should not be null"); final ConversationAvatarData conversationAvatar = Objects.requireNonNull(conversationHeaderData.getConversationAvatar(), /* message = */"conversationAvatar should not be null"); if (conversationAvatar instanceof OneToOneConversationAvatarData oneToOneAvatar) { mConversationFacePile.setVisibility(GONE); mConversationIconView.setVisibility(VISIBLE); mConversationIconView.setImageDrawable(oneToOneAvatar.mDrawable); setSize(mConversationIconView, mSingleAvatarSize); } else { mConversationSenderName.setVisibility(VISIBLE); mConversationSenderName.setText(conversationLayout.getConversationSenderName()); } CharSequence conversationText = conversationLayout.getConversationText(); if (TextUtils.isEmpty(conversationText)) { conversationText = text; // If there isn't an icon, generate a "face pile" based on the sender avatars mConversationIconView.setVisibility(GONE); mConversationFacePile.setVisibility(VISIBLE); final GroupConversationAvatarData groupAvatar = (GroupConversationAvatarData) conversationAvatar; mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile); final ImageView facePileBottomBg = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom_background); final ImageView facePileBottom = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom); final ImageView facePileTop = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_top); conversationLayout.bindFacePileWithDrawable(facePileBottomBg, facePileBottom, facePileTop, groupAvatar); setSize(mConversationFacePile, mFacePileSize); setSize(facePileBottom, mFacePileAvatarSize); setSize(facePileTop, mFacePileAvatarSize); setSize(facePileBottomBg, mFacePileAvatarSize + 2 * mFacePileProtectionWidth); mTransformationHelper.addViewTransformingToSimilar(facePileTop); mTransformationHelper.addViewTransformingToSimilar(facePileBottom); mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg); } super.bind(conversationTitle, conversationText, conversationLayout); } /** Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/ConversationStyleSetAvatarAsync.kt 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.shared import android.widget.flags.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the conversation style set avatar async flag state. */ @Suppress("NOTHING_TO_INLINE") object ConversationStyleSetAvatarAsync { const val FLAG_NAME = Flags.FLAG_CONVERSATION_STYLE_SET_AVATAR_ASYNC /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** Is async hybrid (single-line) view inflation enabled */ @JvmStatic inline val isEnabled get() = Flags.conversationStyleSetAvatarAsync() /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) } Loading
core/java/com/android/internal/widget/ConversationAvatarData.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,9 +21,9 @@ import android.graphics.drawable.Drawable; /** * @hide */ interface ConversationAvatarData { public interface ConversationAvatarData { final class OneToOneConversationAvatarData implements ConversationAvatarData { final Drawable mDrawable; public final Drawable mDrawable; OneToOneConversationAvatarData(Drawable drawable) { mDrawable = drawable; Loading
core/java/com/android/internal/widget/ConversationHeaderData.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.annotation.Nullable; /** * @hide */ final class ConversationHeaderData { public final class ConversationHeaderData { private final CharSequence mConversationText; private final ConversationAvatarData mConversationAvatarData; Loading @@ -38,7 +38,7 @@ final class ConversationHeaderData { } @Nullable ConversationAvatarData getConversationAvatar() { public ConversationAvatarData getConversationAvatar() { return mConversationAvatarData; } }
core/java/com/android/internal/widget/ConversationLayout.java +12 −1 Original line number Diff line number Diff line Loading @@ -162,6 +162,8 @@ public class ConversationLayout extends FrameLayout private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this); private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>(); private boolean mPrecomputedTextEnabled = false; @Nullable private ConversationHeaderData mConversationHeaderData; public ConversationLayout(@NonNull Context context) { super(context); Loading Loading @@ -651,6 +653,7 @@ public class ConversationLayout extends FrameLayout private void setConversationAvatarAndNameFromData( ConversationHeaderData conversationHeaderData) { mConversationHeaderData = conversationHeaderData; final OneToOneConversationAvatarData oneToOneConversationDrawable; final GroupConversationAvatarData groupConversationAvatarData; final ConversationAvatarData conversationAvatar = Loading Loading @@ -804,7 +807,10 @@ public class ConversationLayout extends FrameLayout bottomBackground.setLayoutParams(layoutParams); } private void bindFacePileWithDrawable(ImageView bottomBackground, ImageView bottomView, /** * Binds group avatar drawables to face pile. */ public void bindFacePileWithDrawable(ImageView bottomBackground, ImageView bottomView, ImageView topView, GroupConversationAvatarData groupConversationAvatarData) { applyNotificationBackgroundColor(bottomBackground); bottomView.setImageDrawable(groupConversationAvatarData.mLastIcon); Loading Loading @@ -1573,6 +1579,11 @@ public class ConversationLayout extends FrameLayout return mConversationIcon; } @Nullable public ConversationHeaderData getConversationHeaderData() { return mConversationHeaderData; } private static class TouchDelegateComposite extends TouchDelegate { private final ArrayList<TouchDelegate> mDelegates = new ArrayList<>(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java +82 −18 Original line number Diff line number Diff line Loading @@ -30,14 +30,21 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ConversationAvatarData; import com.android.internal.widget.ConversationAvatarData.GroupConversationAvatarData; import com.android.internal.widget.ConversationAvatarData.OneToOneConversationAvatarData; import com.android.internal.widget.ConversationHeaderData; import com.android.internal.widget.ConversationLayout; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.shared.ConversationStyleSetAvatarAsync; import com.android.systemui.statusbar.notification.row.ui.viewmodel.ConversationAvatar; import com.android.systemui.statusbar.notification.row.ui.viewmodel.FacePile; import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleIcon; import java.util.Objects; /** * A hybrid view which may contain information about one ore more conversations. */ Loading Loading @@ -111,7 +118,38 @@ public class HybridConversationNotificationView extends HybridNotificationView { } ConversationLayout conversationLayout = (ConversationLayout) contentView; Icon conversationIcon = conversationLayout.getConversationIcon(); loadConversationAvatar(conversationLayout); 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 void loadConversationAvatar(ConversationLayout conversationLayout) { AsyncHybridViewInflation.assertInLegacyMode(); if (ConversationStyleSetAvatarAsync.isEnabled()) { loadConversationAvatarWithDrawable(conversationLayout); } else { loadConversationAvatarWithIcon(conversationLayout); } } @Deprecated private void loadConversationAvatarWithIcon(ConversationLayout conversationLayout) { ConversationStyleSetAvatarAsync.assertInLegacyMode(); AsyncHybridViewInflation.assertInLegacyMode(); final Icon conversationIcon = conversationLayout.getConversationIcon(); if (conversationIcon != null) { mConversationFacePile.setVisibility(GONE); mConversationIconView.setVisibility(VISIBLE); Loading @@ -124,11 +162,11 @@ public class HybridConversationNotificationView extends HybridNotificationView { mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile); ImageView facePileBottomBg = mConversationFacePile.requireViewById( final ImageView facePileBottomBg = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom_background); ImageView facePileBottom = mConversationFacePile.requireViewById( final ImageView facePileBottom = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom); ImageView facePileTop = mConversationFacePile.requireViewById( final ImageView facePileTop = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_top); conversationLayout.bindFacePile(facePileBottomBg, facePileBottom, facePileTop); setSize(mConversationFacePile, mFacePileSize); Loading @@ -139,21 +177,47 @@ public class HybridConversationNotificationView extends HybridNotificationView { mTransformationHelper.addViewTransformingToSimilar(facePileBottom); mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg); } CharSequence conversationTitle = conversationLayout.getConversationTitle(); if (TextUtils.isEmpty(conversationTitle)) { conversationTitle = title; } if (conversationLayout.isOneToOne()) { mConversationSenderName.setVisibility(GONE); private void loadConversationAvatarWithDrawable(ConversationLayout conversationLayout) { AsyncHybridViewInflation.assertInLegacyMode(); final ConversationHeaderData conversationHeaderData = Objects.requireNonNull( conversationLayout.getConversationHeaderData(), /* message = */ "conversationHeaderData should not be null"); final ConversationAvatarData conversationAvatar = Objects.requireNonNull(conversationHeaderData.getConversationAvatar(), /* message = */"conversationAvatar should not be null"); if (conversationAvatar instanceof OneToOneConversationAvatarData oneToOneAvatar) { mConversationFacePile.setVisibility(GONE); mConversationIconView.setVisibility(VISIBLE); mConversationIconView.setImageDrawable(oneToOneAvatar.mDrawable); setSize(mConversationIconView, mSingleAvatarSize); } else { mConversationSenderName.setVisibility(VISIBLE); mConversationSenderName.setText(conversationLayout.getConversationSenderName()); } CharSequence conversationText = conversationLayout.getConversationText(); if (TextUtils.isEmpty(conversationText)) { conversationText = text; // If there isn't an icon, generate a "face pile" based on the sender avatars mConversationIconView.setVisibility(GONE); mConversationFacePile.setVisibility(VISIBLE); final GroupConversationAvatarData groupAvatar = (GroupConversationAvatarData) conversationAvatar; mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile); final ImageView facePileBottomBg = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom_background); final ImageView facePileBottom = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_bottom); final ImageView facePileTop = mConversationFacePile.requireViewById( com.android.internal.R.id.conversation_face_pile_top); conversationLayout.bindFacePileWithDrawable(facePileBottomBg, facePileBottom, facePileTop, groupAvatar); setSize(mConversationFacePile, mFacePileSize); setSize(facePileBottom, mFacePileAvatarSize); setSize(facePileTop, mFacePileAvatarSize); setSize(facePileBottomBg, mFacePileAvatarSize + 2 * mFacePileProtectionWidth); mTransformationHelper.addViewTransformingToSimilar(facePileTop); mTransformationHelper.addViewTransformingToSimilar(facePileBottom); mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg); } super.bind(conversationTitle, conversationText, conversationLayout); } /** Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/ConversationStyleSetAvatarAsync.kt 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.shared import android.widget.flags.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the conversation style set avatar async flag state. */ @Suppress("NOTHING_TO_INLINE") object ConversationStyleSetAvatarAsync { const val FLAG_NAME = Flags.FLAG_CONVERSATION_STYLE_SET_AVATAR_ASYNC /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** Is async hybrid (single-line) view inflation enabled */ @JvmStatic inline val isEnabled get() = Flags.conversationStyleSetAvatarAsync() /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) }