740 lines
27 KiB
Java
740 lines
27 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/
|
|
|
|
package com.android.internal.widget;
|
|
|
|
import android.annotation.AttrRes;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.StyleRes;
|
|
import android.app.Flags;
|
|
import android.app.Person;
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Color;
|
|
import android.graphics.Point;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Icon;
|
|
import android.text.TextUtils;
|
|
import android.util.AttributeSet;
|
|
import android.util.DisplayMetrics;
|
|
import android.util.TypedValue;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewParent;
|
|
import android.view.ViewTreeObserver;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.RemoteViews;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.internal.R;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* A message of a {@link MessagingLayout}.
|
|
*/
|
|
@RemoteViews.RemoteView
|
|
public class MessagingGroup extends NotificationOptimizedLinearLayout implements
|
|
MessagingLinearLayout.MessagingChild {
|
|
|
|
private static final MessagingPool<MessagingGroup> sInstancePool =
|
|
new MessagingPool<>(10);
|
|
|
|
/**
|
|
* Images are displayed inline.
|
|
*/
|
|
public static final int IMAGE_DISPLAY_LOCATION_INLINE = 0;
|
|
|
|
/**
|
|
* Images are displayed at the end of the group.
|
|
*/
|
|
public static final int IMAGE_DISPLAY_LOCATION_AT_END = 1;
|
|
|
|
/**
|
|
* Images are displayed externally.
|
|
*/
|
|
public static final int IMAGE_DISPLAY_LOCATION_EXTERNAL = 2;
|
|
|
|
|
|
private MessagingLinearLayout mMessageContainer;
|
|
ImageFloatingTextView mSenderView;
|
|
private ImageView mAvatarView;
|
|
private View mAvatarContainer;
|
|
private String mAvatarSymbol = "";
|
|
private int mLayoutColor;
|
|
private CharSequence mAvatarName = "";
|
|
private Icon mAvatarIcon;
|
|
private int mTextColor;
|
|
private int mSendingTextColor;
|
|
private List<MessagingMessage> mMessages;
|
|
private ArrayList<MessagingMessage> mAddedMessages = new ArrayList<>();
|
|
private boolean mFirstLayout;
|
|
private boolean mIsHidingAnimated;
|
|
private boolean mNeedsGeneratedAvatar;
|
|
private Person mSender;
|
|
private @ImageDisplayLocation int mImageDisplayLocation;
|
|
private ViewGroup mImageContainer;
|
|
private MessagingImageMessage mIsolatedMessage;
|
|
private boolean mClippingDisabled;
|
|
private Point mDisplaySize = new Point();
|
|
private ProgressBar mSendingSpinner;
|
|
private View mSendingSpinnerContainer;
|
|
private boolean mShowingAvatar = true;
|
|
private CharSequence mSenderName;
|
|
private boolean mSingleLine = false;
|
|
private LinearLayout mContentContainer;
|
|
private int mRequestedMaxDisplayedLines = Integer.MAX_VALUE;
|
|
private int mSenderTextPaddingSingleLine;
|
|
private boolean mIsFirstGroupInLayout = true;
|
|
private boolean mCanHideSenderIfFirst;
|
|
private boolean mIsInConversation = true;
|
|
private ViewGroup mMessagingIconContainer;
|
|
private int mConversationContentStart;
|
|
private int mNonConversationContentStart;
|
|
private int mNonConversationPaddingStart;
|
|
private int mConversationAvatarSize;
|
|
private int mNonConversationAvatarSize;
|
|
private int mNotificationTextMarginTop;
|
|
|
|
public MessagingGroup(@NonNull Context context) {
|
|
super(context);
|
|
}
|
|
|
|
public MessagingGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
|
|
super(context, attrs);
|
|
}
|
|
|
|
public MessagingGroup(@NonNull Context context, @Nullable AttributeSet attrs,
|
|
@AttrRes int defStyleAttr) {
|
|
super(context, attrs, defStyleAttr);
|
|
}
|
|
|
|
public MessagingGroup(@NonNull Context context, @Nullable AttributeSet attrs,
|
|
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
super.onFinishInflate();
|
|
mMessageContainer = findViewById(R.id.group_message_container);
|
|
mSenderView = findViewById(R.id.message_name);
|
|
mAvatarView = findViewById(R.id.message_icon);
|
|
mImageContainer = findViewById(R.id.messaging_group_icon_container);
|
|
mSendingSpinner = findViewById(R.id.messaging_group_sending_progress);
|
|
mMessagingIconContainer = findViewById(R.id.message_icon_container);
|
|
mContentContainer = findViewById(R.id.messaging_group_content_container);
|
|
mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container);
|
|
Resources res = getResources();
|
|
DisplayMetrics displayMetrics = res.getDisplayMetrics();
|
|
mDisplaySize.x = displayMetrics.widthPixels;
|
|
mDisplaySize.y = displayMetrics.heightPixels;
|
|
mSenderTextPaddingSingleLine = res.getDimensionPixelSize(
|
|
R.dimen.messaging_group_singleline_sender_padding_end);
|
|
mConversationContentStart = res.getDimensionPixelSize(R.dimen.conversation_content_start);
|
|
mNonConversationContentStart = res.getDimensionPixelSize(
|
|
R.dimen.notification_content_margin_start);
|
|
mNonConversationPaddingStart = res.getDimensionPixelSize(
|
|
R.dimen.messaging_layout_icon_padding_start);
|
|
mConversationAvatarSize = res.getDimensionPixelSize(R.dimen.messaging_avatar_size);
|
|
mNonConversationAvatarSize = res.getDimensionPixelSize(
|
|
R.dimen.notification_icon_circle_size);
|
|
mNotificationTextMarginTop = res.getDimensionPixelSize(
|
|
R.dimen.notification_text_margin_top);
|
|
}
|
|
|
|
public void updateClipRect() {
|
|
// We want to clip to the senderName if it's available, otherwise our images will come
|
|
// from a weird position
|
|
Rect clipRect;
|
|
if (mSenderView.getVisibility() != View.GONE && !mClippingDisabled) {
|
|
int top;
|
|
if (mSingleLine) {
|
|
top = 0;
|
|
} else {
|
|
top = getDistanceFromParent(mSenderView, mContentContainer)
|
|
- getDistanceFromParent(mMessageContainer, mContentContainer)
|
|
+ mSenderView.getHeight();
|
|
}
|
|
int size = Math.max(mDisplaySize.x, mDisplaySize.y);
|
|
clipRect = new Rect(-size, top, size, size);
|
|
} else {
|
|
clipRect = null;
|
|
}
|
|
mMessageContainer.setClipBounds(clipRect);
|
|
}
|
|
|
|
private int getDistanceFromParent(View searchedView, ViewGroup parent) {
|
|
int position = 0;
|
|
View view = searchedView;
|
|
while(view != parent) {
|
|
position += view.getTop() + view.getTranslationY();
|
|
view = (View) view.getParent();
|
|
}
|
|
return position;
|
|
}
|
|
|
|
public void setSender(Person sender, CharSequence nameOverride) {
|
|
mSender = sender;
|
|
if (nameOverride == null) {
|
|
nameOverride = sender.getName();
|
|
}
|
|
if (Flags.cleanUpSpansAndNewLines() && nameOverride != null) {
|
|
// remove formatting from sender name
|
|
nameOverride = nameOverride.toString();
|
|
}
|
|
mSenderName = nameOverride;
|
|
if (mSingleLine && !TextUtils.isEmpty(nameOverride)) {
|
|
nameOverride = mContext.getResources().getString(
|
|
R.string.conversation_single_line_name_display, nameOverride);
|
|
}
|
|
mSenderView.setText(nameOverride);
|
|
mNeedsGeneratedAvatar = sender.getIcon() == null;
|
|
if (!mNeedsGeneratedAvatar) {
|
|
setAvatar(sender.getIcon());
|
|
}
|
|
updateSenderVisibility();
|
|
}
|
|
|
|
/**
|
|
* Should the avatar be shown for this view.
|
|
*
|
|
* @param showingAvatar should it be shown
|
|
*/
|
|
public void setShowingAvatar(boolean showingAvatar) {
|
|
mAvatarView.setVisibility(showingAvatar ? VISIBLE : GONE);
|
|
mShowingAvatar = showingAvatar;
|
|
}
|
|
|
|
public void setSending(boolean sending) {
|
|
int visibility = sending ? VISIBLE : GONE;
|
|
if (mSendingSpinnerContainer.getVisibility() != visibility) {
|
|
mSendingSpinnerContainer.setVisibility(visibility);
|
|
updateMessageColor();
|
|
}
|
|
}
|
|
|
|
private int calculateSendingTextColor() {
|
|
TypedValue alphaValue = new TypedValue();
|
|
mContext.getResources().getValue(
|
|
R.dimen.notification_secondary_text_disabled_alpha, alphaValue, true);
|
|
float alpha = alphaValue.getFloat();
|
|
return Color.valueOf(
|
|
Color.red(mTextColor),
|
|
Color.green(mTextColor),
|
|
Color.blue(mTextColor),
|
|
alpha).toArgb();
|
|
}
|
|
|
|
public void setAvatar(Icon icon) {
|
|
mAvatarIcon = icon;
|
|
if (mShowingAvatar || icon == null) {
|
|
mAvatarView.setImageIcon(icon);
|
|
}
|
|
mAvatarSymbol = "";
|
|
mAvatarName = "";
|
|
}
|
|
|
|
static MessagingGroup createGroup(MessagingLinearLayout layout) {;
|
|
MessagingGroup createdGroup = sInstancePool.acquire();
|
|
if (createdGroup == null) {
|
|
createdGroup = (MessagingGroup) LayoutInflater.from(layout.getContext()).inflate(
|
|
R.layout.notification_template_messaging_group, layout,
|
|
false);
|
|
createdGroup.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
|
|
}
|
|
layout.addView(createdGroup);
|
|
return createdGroup;
|
|
}
|
|
|
|
public void removeMessage(MessagingMessage messagingMessage,
|
|
ArrayList<MessagingLinearLayout.MessagingChild> toRecycle) {
|
|
View view = messagingMessage.getView();
|
|
boolean wasShown = view.isShown();
|
|
ViewGroup messageParent = (ViewGroup) view.getParent();
|
|
if (messageParent == null) {
|
|
return;
|
|
}
|
|
messageParent.removeView(view);
|
|
if (wasShown && !MessagingLinearLayout.isGone(view)) {
|
|
messageParent.addTransientView(view, 0);
|
|
performRemoveAnimation(view, () -> {
|
|
messageParent.removeTransientView(view);
|
|
messagingMessage.recycle();
|
|
});
|
|
} else {
|
|
toRecycle.add(messagingMessage);
|
|
}
|
|
}
|
|
|
|
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);
|
|
mSenderView.setAlpha(1.0f);
|
|
mSenderView.setTranslationY(0.0f);
|
|
setAlpha(1.0f);
|
|
mIsolatedMessage = null;
|
|
mMessages = null;
|
|
mSenderName = null;
|
|
mAddedMessages.clear();
|
|
mFirstLayout = true;
|
|
setCanHideSenderIfFirst(false);
|
|
setIsFirstInLayout(true);
|
|
|
|
setMaxDisplayedLines(Integer.MAX_VALUE);
|
|
setSingleLine(false);
|
|
setShowingAvatar(true);
|
|
MessagingPropertyAnimator.recycle(this);
|
|
sInstancePool.release(MessagingGroup.this);
|
|
}
|
|
|
|
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) {
|
|
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() {
|
|
return mSenderName;
|
|
}
|
|
|
|
public static void dropCache() {
|
|
sInstancePool.clear();
|
|
}
|
|
|
|
@Override
|
|
public int getMeasuredType() {
|
|
if (mIsolatedMessage != null) {
|
|
// We only want to show one group if we have an inline image, so let's return shortened
|
|
// to avoid displaying the other ones.
|
|
return MEASURED_SHORTENED;
|
|
}
|
|
boolean hasNormal = false;
|
|
for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) {
|
|
View child = mMessageContainer.getChildAt(i);
|
|
if (child.getVisibility() == GONE) {
|
|
continue;
|
|
}
|
|
if (child instanceof MessagingLinearLayout.MessagingChild) {
|
|
int type = ((MessagingLinearLayout.MessagingChild) child).getMeasuredType();
|
|
boolean tooSmall = type == MEASURED_TOO_SMALL;
|
|
final MessagingLinearLayout.LayoutParams lp =
|
|
(MessagingLinearLayout.LayoutParams) child.getLayoutParams();
|
|
tooSmall |= lp.hide;
|
|
if (tooSmall) {
|
|
if (hasNormal) {
|
|
return MEASURED_SHORTENED;
|
|
} else {
|
|
return MEASURED_TOO_SMALL;
|
|
}
|
|
} else if (type == MEASURED_SHORTENED) {
|
|
return MEASURED_SHORTENED;
|
|
} else {
|
|
hasNormal = true;
|
|
}
|
|
}
|
|
}
|
|
return MEASURED_NORMAL;
|
|
}
|
|
|
|
@Override
|
|
public int getConsumedLines() {
|
|
int result = 0;
|
|
for (int i = 0; i < mMessageContainer.getChildCount(); i++) {
|
|
View child = mMessageContainer.getChildAt(i);
|
|
if (child instanceof MessagingLinearLayout.MessagingChild) {
|
|
result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines();
|
|
}
|
|
}
|
|
result = mIsolatedMessage != null ? Math.max(result, 1) : result;
|
|
// A group is usually taking up quite some space with the padding and the name, let's add 1
|
|
return result + 1;
|
|
}
|
|
|
|
@Override
|
|
public void setMaxDisplayedLines(int lines) {
|
|
mRequestedMaxDisplayedLines = lines;
|
|
updateMaxDisplayedLines();
|
|
}
|
|
|
|
private void updateMaxDisplayedLines() {
|
|
mMessageContainer.setMaxDisplayedLines(mSingleLine ? 1 : mRequestedMaxDisplayedLines);
|
|
}
|
|
|
|
@Override
|
|
public void hideAnimated() {
|
|
setIsHidingAnimated(true);
|
|
removeGroupAnimated(() -> setIsHidingAnimated(false));
|
|
}
|
|
|
|
@Override
|
|
public boolean isHidingAnimated() {
|
|
return mIsHidingAnimated;
|
|
}
|
|
|
|
@Override
|
|
public void setIsFirstInLayout(boolean first) {
|
|
if (first != mIsFirstGroupInLayout) {
|
|
mIsFirstGroupInLayout = first;
|
|
updateSenderVisibility();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param canHide true if the sender can be hidden if it is first
|
|
*/
|
|
public void setCanHideSenderIfFirst(boolean canHide) {
|
|
if (mCanHideSenderIfFirst != canHide) {
|
|
mCanHideSenderIfFirst = canHide;
|
|
updateSenderVisibility();
|
|
}
|
|
}
|
|
|
|
private void updateSenderVisibility() {
|
|
boolean hidden = (mIsFirstGroupInLayout || mSingleLine) && mCanHideSenderIfFirst
|
|
|| TextUtils.isEmpty(mSenderName);
|
|
mSenderView.setVisibility(hidden ? GONE : VISIBLE);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasDifferentHeightWhenFirst() {
|
|
return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName);
|
|
}
|
|
|
|
private void setIsHidingAnimated(boolean isHiding) {
|
|
ViewParent parent = getParent();
|
|
mIsHidingAnimated = isHiding;
|
|
invalidate();
|
|
if (parent instanceof ViewGroup) {
|
|
((ViewGroup) parent).invalidate();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasOverlappingRendering() {
|
|
return false;
|
|
}
|
|
|
|
public Icon getAvatarSymbolIfMatching(CharSequence avatarName, String avatarSymbol,
|
|
int layoutColor) {
|
|
if (mAvatarName.equals(avatarName) && mAvatarSymbol.equals(avatarSymbol)
|
|
&& layoutColor == mLayoutColor) {
|
|
return mAvatarIcon;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void setCreatedAvatar(Icon cachedIcon, CharSequence avatarName, String avatarSymbol,
|
|
int layoutColor) {
|
|
if (!mAvatarName.equals(avatarName) || !mAvatarSymbol.equals(avatarSymbol)
|
|
|| layoutColor != mLayoutColor) {
|
|
setAvatar(cachedIcon);
|
|
mAvatarSymbol = avatarSymbol;
|
|
setLayoutColor(layoutColor);
|
|
mAvatarName = avatarName;
|
|
}
|
|
}
|
|
|
|
public void setTextColors(int senderTextColor, int messageTextColor) {
|
|
mTextColor = messageTextColor;
|
|
mSendingTextColor = calculateSendingTextColor();
|
|
updateMessageColor();
|
|
mSenderView.setTextColor(senderTextColor);
|
|
}
|
|
|
|
public void setLayoutColor(int layoutColor) {
|
|
if (layoutColor != mLayoutColor){
|
|
mLayoutColor = layoutColor;
|
|
mSendingSpinner.setIndeterminateTintList(ColorStateList.valueOf(mLayoutColor));
|
|
}
|
|
}
|
|
|
|
private void updateMessageColor() {
|
|
if (mMessages != null) {
|
|
int color = mSendingSpinnerContainer.getVisibility() == View.VISIBLE
|
|
? mSendingTextColor : mTextColor;
|
|
for (MessagingMessage message : mMessages) {
|
|
final boolean isRemoteInputHistory =
|
|
message.getMessage() != null && message.getMessage().isRemoteInputHistory();
|
|
message.setColor(isRemoteInputHistory ? color : mTextColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setMessages(List<MessagingMessage> group) {
|
|
// Let's now make sure all children are added and in the correct order
|
|
int textMessageIndex = 0;
|
|
MessagingImageMessage isolatedMessage = null;
|
|
for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) {
|
|
MessagingMessage message = group.get(messageIndex);
|
|
if (message.getGroup() != this) {
|
|
message.setMessagingGroup(this);
|
|
mAddedMessages.add(message);
|
|
}
|
|
boolean isImage = message instanceof MessagingImageMessage;
|
|
if (mImageDisplayLocation != IMAGE_DISPLAY_LOCATION_INLINE && isImage) {
|
|
isolatedMessage = (MessagingImageMessage) message;
|
|
} else {
|
|
if (removeFromParentIfDifferent(message, mMessageContainer)) {
|
|
ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams();
|
|
if (layoutParams != null
|
|
&& !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) {
|
|
message.getView().setLayoutParams(
|
|
mMessageContainer.generateDefaultLayoutParams());
|
|
}
|
|
mMessageContainer.addView(message.getView(), textMessageIndex);
|
|
}
|
|
if (isImage) {
|
|
((MessagingImageMessage) message).setIsolated(false);
|
|
}
|
|
// Let's sort them properly
|
|
if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) {
|
|
mMessageContainer.removeView(message.getView());
|
|
mMessageContainer.addView(message.getView(), textMessageIndex);
|
|
}
|
|
textMessageIndex++;
|
|
}
|
|
}
|
|
if (isolatedMessage != null) {
|
|
if (mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_AT_END
|
|
&& removeFromParentIfDifferent(isolatedMessage, mImageContainer)) {
|
|
mImageContainer.removeAllViews();
|
|
mImageContainer.addView(isolatedMessage.getView());
|
|
} else if (mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_EXTERNAL) {
|
|
mImageContainer.removeAllViews();
|
|
}
|
|
isolatedMessage.setIsolated(true);
|
|
} else if (mIsolatedMessage != null) {
|
|
mImageContainer.removeAllViews();
|
|
}
|
|
mIsolatedMessage = isolatedMessage;
|
|
updateImageContainerVisibility();
|
|
mMessages = group;
|
|
updateMessageColor();
|
|
}
|
|
|
|
private void updateImageContainerVisibility() {
|
|
mImageContainer.setVisibility(mIsolatedMessage != null
|
|
&& mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_AT_END
|
|
? View.VISIBLE : View.GONE);
|
|
}
|
|
|
|
/**
|
|
* Remove the message from the parent if the parent isn't the one provided
|
|
* @return whether the message was removed
|
|
*/
|
|
private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) {
|
|
ViewParent parent = message.getView().getParent();
|
|
if (parent != newParent) {
|
|
if (parent instanceof ViewGroup) {
|
|
((ViewGroup) parent).removeView(message.getView());
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
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() {
|
|
for (MessagingMessage message : mAddedMessages) {
|
|
if (!message.getView().isShown()) {
|
|
continue;
|
|
}
|
|
MessagingPropertyAnimator.fadeIn(message.getView());
|
|
if (!firstLayout) {
|
|
MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(),
|
|
message.getView().getHeight(),
|
|
MessagingLayout.LINEAR_OUT_SLOW_IN);
|
|
}
|
|
}
|
|
mAddedMessages.clear();
|
|
getViewTreeObserver().removeOnPreDrawListener(this);
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
mFirstLayout = false;
|
|
updateClipRect();
|
|
}
|
|
|
|
/**
|
|
* Calculates the group compatibility between this and another group.
|
|
*
|
|
* @param otherGroup the other group to compare it with
|
|
*
|
|
* @return 0 if the groups are totally incompatible or 1 + the number of matching messages if
|
|
* they match.
|
|
*/
|
|
public int calculateGroupCompatibility(MessagingGroup otherGroup) {
|
|
if (TextUtils.equals(getSenderName(),otherGroup.getSenderName())) {
|
|
int result = 1;
|
|
for (int i = 0; i < mMessages.size() && i < otherGroup.mMessages.size(); i++) {
|
|
MessagingMessage ownMessage = mMessages.get(mMessages.size() - 1 - i);
|
|
MessagingMessage otherMessage = otherGroup.mMessages.get(
|
|
otherGroup.mMessages.size() - 1 - i);
|
|
if (!ownMessage.sameAs(otherMessage)) {
|
|
return result;
|
|
}
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public TextView getSenderView() {
|
|
return mSenderView;
|
|
}
|
|
|
|
public View getAvatar() {
|
|
return mAvatarView;
|
|
}
|
|
|
|
public Icon getAvatarIcon() {
|
|
return mAvatarIcon;
|
|
}
|
|
|
|
public MessagingLinearLayout getMessageContainer() {
|
|
return mMessageContainer;
|
|
}
|
|
|
|
public MessagingImageMessage getIsolatedMessage() {
|
|
return mIsolatedMessage;
|
|
}
|
|
|
|
public boolean needsGeneratedAvatar() {
|
|
return mNeedsGeneratedAvatar;
|
|
}
|
|
|
|
public Person getSender() {
|
|
return mSender;
|
|
}
|
|
|
|
public void setClippingDisabled(boolean disabled) {
|
|
mClippingDisabled = disabled;
|
|
}
|
|
|
|
public void setImageDisplayLocation(@ImageDisplayLocation int displayLocation) {
|
|
if (mImageDisplayLocation != displayLocation) {
|
|
mImageDisplayLocation = displayLocation;
|
|
updateImageContainerVisibility();
|
|
}
|
|
}
|
|
|
|
public List<MessagingMessage> getMessages() {
|
|
return mMessages;
|
|
}
|
|
|
|
/**
|
|
* Set this layout to be single line and therefore displaying both the sender and the text on
|
|
* the same line.
|
|
*
|
|
* @param singleLine should be layout be single line
|
|
*/
|
|
public void setSingleLine(boolean singleLine) {
|
|
if (singleLine != mSingleLine) {
|
|
mSingleLine = singleLine;
|
|
MarginLayoutParams p = (MarginLayoutParams) mMessageContainer.getLayoutParams();
|
|
p.topMargin = singleLine ? 0 : mNotificationTextMarginTop;
|
|
mMessageContainer.setLayoutParams(p);
|
|
mContentContainer.setOrientation(
|
|
singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
|
|
MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams();
|
|
layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0);
|
|
mSenderView.setSingleLine(singleLine);
|
|
updateMaxDisplayedLines();
|
|
updateClipRect();
|
|
updateSenderVisibility();
|
|
}
|
|
}
|
|
|
|
public boolean isSingleLine() {
|
|
return mSingleLine;
|
|
}
|
|
|
|
/**
|
|
* Set this group to be displayed in a conversation and adjust the visual appearance
|
|
*
|
|
* @param isInConversation is this in a conversation
|
|
*/
|
|
public void setIsInConversation(boolean isInConversation) {
|
|
if (mIsInConversation != isInConversation) {
|
|
mIsInConversation = isInConversation;
|
|
MarginLayoutParams layoutParams =
|
|
(MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
|
|
layoutParams.width = mIsInConversation
|
|
? mConversationContentStart
|
|
: mNonConversationContentStart;
|
|
mMessagingIconContainer.setLayoutParams(layoutParams);
|
|
int imagePaddingStart = isInConversation ? 0 : mNonConversationPaddingStart;
|
|
mMessagingIconContainer.setPaddingRelative(imagePaddingStart, 0, 0, 0);
|
|
|
|
ViewGroup.LayoutParams avatarLayoutParams = mAvatarView.getLayoutParams();
|
|
int size = mIsInConversation ? mConversationAvatarSize : mNonConversationAvatarSize;
|
|
avatarLayoutParams.height = size;
|
|
avatarLayoutParams.width = size;
|
|
mAvatarView.setLayoutParams(avatarLayoutParams);
|
|
}
|
|
}
|
|
|
|
@IntDef(prefix = {"IMAGE_DISPLAY_LOCATION_"}, value = {
|
|
IMAGE_DISPLAY_LOCATION_INLINE,
|
|
IMAGE_DISPLAY_LOCATION_AT_END,
|
|
IMAGE_DISPLAY_LOCATION_EXTERNAL
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
private @interface ImageDisplayLocation {
|
|
}
|
|
}
|