Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f68af9b1 authored by Selim Cinek's avatar Selim Cinek
Browse files

Fixed the animations of Messaging Layout, leading to overlaps

The animations could go wild at times, leading to overlapping
messages and ugly renderings. This improves the animations
overall and fixes those cases.

Test: add messages, observe animations
Fixes: 78114531
Fixes: 80409521
Change-Id: I6f21b87706ccc2e85f1edbd9489e4bf7e686d7d8
parent c0797c85
Loading
Loading
Loading
Loading
+53 −55
Original line number Diff line number Diff line
@@ -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);
@@ -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() {
@@ -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)
@@ -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() {
@@ -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);
+0 −2
Original line number Diff line number Diff line
@@ -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);
+30 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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<>();
+26 −9
Original line number Diff line number Diff line
@@ -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();
@@ -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;
@@ -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
     */
@@ -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);
+2 −3
Original line number Diff line number Diff line
@@ -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() {
@@ -133,7 +132,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
    }

    default void recycle() {
        getState().reset();
        getState().recycle();
    }

    default View getView() {
Loading