Loading core/java/com/android/internal/widget/MessagingGroup.java +53 −55 Original line number Diff line number Diff line Loading @@ -100,7 +100,6 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou super.onFinishInflate(); mMessageContainer = findViewById(R.id.group_message_container); mSenderName = findViewById(R.id.message_name); mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); mAvatarView = findViewById(R.id.message_icon); mImageContainer = findViewById(R.id.messaging_group_icon_container); mSendingSpinner = findViewById(R.id.messaging_group_sending_progress); Loading Loading @@ -190,73 +189,66 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void removeMessage(MessagingMessage messagingMessage) { ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent(); messageParent.removeView(messagingMessage.getView()); View view = messagingMessage.getView(); boolean wasShown = view.isShown(); ViewGroup messageParent = (ViewGroup) view.getParent(); if (messageParent == null) { return; } messageParent.removeView(view); Runnable recycleRunnable = () -> { messageParent.removeTransientView(messagingMessage.getView()); messageParent.removeTransientView(view); messagingMessage.recycle(); if (mMessageContainer.getChildCount() == 0 && mMessageContainer.getTransientViewCount() == 0 && mImageContainer.getChildCount() == 0) { ViewParent parent = getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(MessagingGroup.this); }; if (wasShown && !MessagingLinearLayout.isGone(view)) { messageParent.addTransientView(view, 0); performRemoveAnimation(view, recycleRunnable); } else { recycleRunnable.run(); } } public void recycle() { if (mIsolatedMessage != null) { mImageContainer.removeView(mIsolatedMessage); } for (int i = 0; i < mMessages.size(); i++) { MessagingMessage message = mMessages.get(i); mMessageContainer.removeView(message.getView()); message.recycle(); } setAvatar(null); mAvatarView.setAlpha(1.0f); mAvatarView.setTranslationY(0.0f); mSenderName.setAlpha(1.0f); mSenderName.setTranslationY(0.0f); setAlpha(1.0f); mIsolatedMessage = null; mMessages = null; mAddedMessages.clear(); mFirstLayout = true; MessagingPropertyAnimator.recycle(this); sInstancePool.release(MessagingGroup.this); } }; if (isShown()) { messageParent.addTransientView(messagingMessage.getView(), 0); performRemoveAnimation(messagingMessage.getView(), recycleRunnable); if (mMessageContainer.getChildCount() == 0 && mImageContainer.getChildCount() == 0) { removeGroupAnimated(null); } } else { recycleRunnable.run(); } } private void removeGroupAnimated(Runnable endAction) { performRemoveAnimation(mAvatarView, null); performRemoveAnimation(mSenderName, null); boolean endActionTriggered = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } final ViewGroup.LayoutParams lp = child.getLayoutParams(); if (lp instanceof MessagingLinearLayout.LayoutParams && ((MessagingLinearLayout.LayoutParams) lp).hide && !((MessagingLinearLayout.LayoutParams) lp).visibleBefore) { continue; } Runnable childEndAction = endActionTriggered ? null : endAction; performRemoveAnimation(child, childEndAction); endActionTriggered = true; } if (mIsolatedMessage != null) { performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null); endActionTriggered = true; } if (!endActionTriggered && endAction != null) { public void removeGroupAnimated(Runnable endAction) { performRemoveAnimation(this, () -> { setAlpha(1.0f); MessagingPropertyAnimator.setToLaidOutPosition(this); if (endAction != null) { endAction.run(); } }); } public void performRemoveAnimation(View message, Runnable endAction) { MessagingPropertyAnimator.fadeOut(message, endAction); MessagingPropertyAnimator.startLocalTranslationTo(message, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); performRemoveAnimation(message, -message.getHeight(), endAction); } private void performRemoveAnimation(View view, int disappearTranslation, Runnable endAction) { MessagingPropertyAnimator.startLocalTranslationTo(view, disappearTranslation, MessagingLayout.FAST_OUT_LINEAR_IN); MessagingPropertyAnimator.fadeOut(view, endAction); } public CharSequence getSenderName() { Loading Loading @@ -341,6 +333,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } @Override public boolean hasOverlappingRendering() { return false; } public Icon getAvatarSymbolIfMatching(CharSequence avatarName, String avatarSymbol, int layoutColor) { if (mAvatarName.equals(avatarName) && mAvatarSymbol.equals(avatarSymbol) Loading Loading @@ -458,6 +455,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (!mAddedMessages.isEmpty()) { final boolean firstLayout = mFirstLayout; getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { Loading @@ -466,7 +464,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou continue; } MessagingPropertyAnimator.fadeIn(message.getView()); if (!mFirstLayout) { if (!firstLayout) { MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(), message.getView().getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); Loading core/java/com/android/internal/widget/MessagingImageMessage.java +0 −2 Original line number Diff line number Diff line Loading @@ -170,8 +170,6 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage public void recycle() { MessagingMessage.super.recycle(); setAlpha(1.0f); setTranslationY(0); setImageBitmap(null); mDrawable = null; sInstancePool.release(this); Loading core/java/com/android/internal/widget/MessagingLayout.java +30 −0 Original line number Diff line number Diff line Loading @@ -180,8 +180,13 @@ public class MessagingLayout extends FrameLayout { List<MessagingMessage> historicMessages = createMessages(newHistoricMessages, true /* isHistoric */); List<MessagingMessage> messages = createMessages(newMessages, false /* isHistoric */); ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups); addMessagesToGroups(historicMessages, messages, showSpinner); // Let's first check which groups were removed altogether and remove them in one animation removeGroups(oldGroups); // Let's remove the remaining messages mMessages.forEach(REMOVE_MESSAGE); mHistoricMessages.forEach(REMOVE_MESSAGE); Loading @@ -193,6 +198,31 @@ public class MessagingLayout extends FrameLayout { updateTitleAndNamesDisplay(); } private void removeGroups(ArrayList<MessagingGroup> oldGroups) { int size = oldGroups.size(); for (int i = 0; i < size; i++) { MessagingGroup group = oldGroups.get(i); if (!mGroups.contains(group)) { List<MessagingMessage> messages = group.getMessages(); Runnable endRunnable = () -> { mMessagingLinearLayout.removeTransientView(group); group.recycle(); }; boolean wasShown = group.isShown(); mMessagingLinearLayout.removeView(group); if (wasShown && !MessagingLinearLayout.isGone(group)) { mMessagingLinearLayout.addTransientView(group, 0); group.removeGroupAnimated(endRunnable); } else { endRunnable.run(); } mMessages.removeAll(messages); mHistoricMessages.removeAll(messages); } } } private void updateTitleAndNamesDisplay() { ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>(); Loading core/java/com/android/internal/widget/MessagingLinearLayout.java +26 −9 Original line number Diff line number Diff line Loading @@ -163,15 +163,6 @@ public class MessagingLinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); MessagingChild messagingChild = (MessagingChild) child; if (lp.hide) { if (shown && lp.visibleBefore) { messagingChild.hideAnimated(); } lp.visibleBefore = false; continue; } else { lp.visibleBefore = true; } final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); Loading @@ -182,6 +173,19 @@ public class MessagingLinearLayout extends ViewGroup { } else { childLeft = paddingLeft + lp.leftMargin; } if (lp.hide) { if (shown && lp.visibleBefore) { // We still want to lay out the child to have great animations child.layout(childLeft, childTop, childLeft + childWidth, childTop + lp.lastVisibleHeight); messagingChild.hideAnimated(); } lp.visibleBefore = false; continue; } else { lp.visibleBefore = true; lp.lastVisibleHeight = childHeight; } if (!first) { childTop += mSpacing; Loading Loading @@ -228,6 +232,18 @@ public class MessagingLinearLayout extends ViewGroup { return copy; } public static boolean isGone(View view) { if (view.getVisibility() == View.GONE) { return true; } final ViewGroup.LayoutParams lp = view.getLayoutParams(); if (lp instanceof MessagingLinearLayout.LayoutParams && ((MessagingLinearLayout.LayoutParams) lp).hide) { return true; } return false; } /** * Sets how many lines should be displayed at most */ Loading Loading @@ -263,6 +279,7 @@ public class MessagingLinearLayout extends ViewGroup { public boolean hide = false; public boolean visibleBefore = false; public int lastVisibleHeight; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); Loading core/java/com/android/internal/widget/MessagingMessage.java +2 −3 Original line number Diff line number Diff line Loading @@ -124,8 +124,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { @Override default void hideAnimated() { setIsHidingAnimated(true); getGroup().performRemoveAnimation(getState().getHostView(), () -> setIsHidingAnimated(false)); getGroup().performRemoveAnimation(getView(), () -> setIsHidingAnimated(false)); } default boolean hasOverlappingRendering() { Loading @@ -133,7 +132,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { } default void recycle() { getState().reset(); getState().recycle(); } default View getView() { Loading Loading
core/java/com/android/internal/widget/MessagingGroup.java +53 −55 Original line number Diff line number Diff line Loading @@ -100,7 +100,6 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou super.onFinishInflate(); mMessageContainer = findViewById(R.id.group_message_container); mSenderName = findViewById(R.id.message_name); mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); mAvatarView = findViewById(R.id.message_icon); mImageContainer = findViewById(R.id.messaging_group_icon_container); mSendingSpinner = findViewById(R.id.messaging_group_sending_progress); Loading Loading @@ -190,73 +189,66 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void removeMessage(MessagingMessage messagingMessage) { ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent(); messageParent.removeView(messagingMessage.getView()); View view = messagingMessage.getView(); boolean wasShown = view.isShown(); ViewGroup messageParent = (ViewGroup) view.getParent(); if (messageParent == null) { return; } messageParent.removeView(view); Runnable recycleRunnable = () -> { messageParent.removeTransientView(messagingMessage.getView()); messageParent.removeTransientView(view); messagingMessage.recycle(); if (mMessageContainer.getChildCount() == 0 && mMessageContainer.getTransientViewCount() == 0 && mImageContainer.getChildCount() == 0) { ViewParent parent = getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(MessagingGroup.this); }; if (wasShown && !MessagingLinearLayout.isGone(view)) { messageParent.addTransientView(view, 0); performRemoveAnimation(view, recycleRunnable); } else { recycleRunnable.run(); } } public void recycle() { if (mIsolatedMessage != null) { mImageContainer.removeView(mIsolatedMessage); } for (int i = 0; i < mMessages.size(); i++) { MessagingMessage message = mMessages.get(i); mMessageContainer.removeView(message.getView()); message.recycle(); } setAvatar(null); mAvatarView.setAlpha(1.0f); mAvatarView.setTranslationY(0.0f); mSenderName.setAlpha(1.0f); mSenderName.setTranslationY(0.0f); setAlpha(1.0f); mIsolatedMessage = null; mMessages = null; mAddedMessages.clear(); mFirstLayout = true; MessagingPropertyAnimator.recycle(this); sInstancePool.release(MessagingGroup.this); } }; if (isShown()) { messageParent.addTransientView(messagingMessage.getView(), 0); performRemoveAnimation(messagingMessage.getView(), recycleRunnable); if (mMessageContainer.getChildCount() == 0 && mImageContainer.getChildCount() == 0) { removeGroupAnimated(null); } } else { recycleRunnable.run(); } } private void removeGroupAnimated(Runnable endAction) { performRemoveAnimation(mAvatarView, null); performRemoveAnimation(mSenderName, null); boolean endActionTriggered = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } final ViewGroup.LayoutParams lp = child.getLayoutParams(); if (lp instanceof MessagingLinearLayout.LayoutParams && ((MessagingLinearLayout.LayoutParams) lp).hide && !((MessagingLinearLayout.LayoutParams) lp).visibleBefore) { continue; } Runnable childEndAction = endActionTriggered ? null : endAction; performRemoveAnimation(child, childEndAction); endActionTriggered = true; } if (mIsolatedMessage != null) { performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null); endActionTriggered = true; } if (!endActionTriggered && endAction != null) { public void removeGroupAnimated(Runnable endAction) { performRemoveAnimation(this, () -> { setAlpha(1.0f); MessagingPropertyAnimator.setToLaidOutPosition(this); if (endAction != null) { endAction.run(); } }); } public void performRemoveAnimation(View message, Runnable endAction) { MessagingPropertyAnimator.fadeOut(message, endAction); MessagingPropertyAnimator.startLocalTranslationTo(message, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); performRemoveAnimation(message, -message.getHeight(), endAction); } private void performRemoveAnimation(View view, int disappearTranslation, Runnable endAction) { MessagingPropertyAnimator.startLocalTranslationTo(view, disappearTranslation, MessagingLayout.FAST_OUT_LINEAR_IN); MessagingPropertyAnimator.fadeOut(view, endAction); } public CharSequence getSenderName() { Loading Loading @@ -341,6 +333,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } @Override public boolean hasOverlappingRendering() { return false; } public Icon getAvatarSymbolIfMatching(CharSequence avatarName, String avatarSymbol, int layoutColor) { if (mAvatarName.equals(avatarName) && mAvatarSymbol.equals(avatarSymbol) Loading Loading @@ -458,6 +455,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (!mAddedMessages.isEmpty()) { final boolean firstLayout = mFirstLayout; getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { Loading @@ -466,7 +464,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou continue; } MessagingPropertyAnimator.fadeIn(message.getView()); if (!mFirstLayout) { if (!firstLayout) { MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(), message.getView().getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); Loading
core/java/com/android/internal/widget/MessagingImageMessage.java +0 −2 Original line number Diff line number Diff line Loading @@ -170,8 +170,6 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage public void recycle() { MessagingMessage.super.recycle(); setAlpha(1.0f); setTranslationY(0); setImageBitmap(null); mDrawable = null; sInstancePool.release(this); Loading
core/java/com/android/internal/widget/MessagingLayout.java +30 −0 Original line number Diff line number Diff line Loading @@ -180,8 +180,13 @@ public class MessagingLayout extends FrameLayout { List<MessagingMessage> historicMessages = createMessages(newHistoricMessages, true /* isHistoric */); List<MessagingMessage> messages = createMessages(newMessages, false /* isHistoric */); ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups); addMessagesToGroups(historicMessages, messages, showSpinner); // Let's first check which groups were removed altogether and remove them in one animation removeGroups(oldGroups); // Let's remove the remaining messages mMessages.forEach(REMOVE_MESSAGE); mHistoricMessages.forEach(REMOVE_MESSAGE); Loading @@ -193,6 +198,31 @@ public class MessagingLayout extends FrameLayout { updateTitleAndNamesDisplay(); } private void removeGroups(ArrayList<MessagingGroup> oldGroups) { int size = oldGroups.size(); for (int i = 0; i < size; i++) { MessagingGroup group = oldGroups.get(i); if (!mGroups.contains(group)) { List<MessagingMessage> messages = group.getMessages(); Runnable endRunnable = () -> { mMessagingLinearLayout.removeTransientView(group); group.recycle(); }; boolean wasShown = group.isShown(); mMessagingLinearLayout.removeView(group); if (wasShown && !MessagingLinearLayout.isGone(group)) { mMessagingLinearLayout.addTransientView(group, 0); group.removeGroupAnimated(endRunnable); } else { endRunnable.run(); } mMessages.removeAll(messages); mHistoricMessages.removeAll(messages); } } } private void updateTitleAndNamesDisplay() { ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>(); Loading
core/java/com/android/internal/widget/MessagingLinearLayout.java +26 −9 Original line number Diff line number Diff line Loading @@ -163,15 +163,6 @@ public class MessagingLinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); MessagingChild messagingChild = (MessagingChild) child; if (lp.hide) { if (shown && lp.visibleBefore) { messagingChild.hideAnimated(); } lp.visibleBefore = false; continue; } else { lp.visibleBefore = true; } final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); Loading @@ -182,6 +173,19 @@ public class MessagingLinearLayout extends ViewGroup { } else { childLeft = paddingLeft + lp.leftMargin; } if (lp.hide) { if (shown && lp.visibleBefore) { // We still want to lay out the child to have great animations child.layout(childLeft, childTop, childLeft + childWidth, childTop + lp.lastVisibleHeight); messagingChild.hideAnimated(); } lp.visibleBefore = false; continue; } else { lp.visibleBefore = true; lp.lastVisibleHeight = childHeight; } if (!first) { childTop += mSpacing; Loading Loading @@ -228,6 +232,18 @@ public class MessagingLinearLayout extends ViewGroup { return copy; } public static boolean isGone(View view) { if (view.getVisibility() == View.GONE) { return true; } final ViewGroup.LayoutParams lp = view.getLayoutParams(); if (lp instanceof MessagingLinearLayout.LayoutParams && ((MessagingLinearLayout.LayoutParams) lp).hide) { return true; } return false; } /** * Sets how many lines should be displayed at most */ Loading Loading @@ -263,6 +279,7 @@ public class MessagingLinearLayout extends ViewGroup { public boolean hide = false; public boolean visibleBefore = false; public int lastVisibleHeight; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); Loading
core/java/com/android/internal/widget/MessagingMessage.java +2 −3 Original line number Diff line number Diff line Loading @@ -124,8 +124,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { @Override default void hideAnimated() { setIsHidingAnimated(true); getGroup().performRemoveAnimation(getState().getHostView(), () -> setIsHidingAnimated(false)); getGroup().performRemoveAnimation(getView(), () -> setIsHidingAnimated(false)); } default boolean hasOverlappingRendering() { Loading @@ -133,7 +132,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { } default void recycle() { getState().reset(); getState().recycle(); } default View getView() { Loading