262 lines
11 KiB
Java
262 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2016 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.settings.display;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.util.AttributeSet;
|
|
import android.view.Gravity;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.settings.R;
|
|
|
|
/**
|
|
* The view for a single entry in a conversation. This is a simplified version of
|
|
* com.android.messaging.ui.conversation.ConversationMessageView class.
|
|
*/
|
|
public class ConversationMessageView extends FrameLayout {
|
|
private final boolean mIncoming;
|
|
private final CharSequence mMessageText;
|
|
private final CharSequence mTimestampText;
|
|
private final CharSequence mIconText;
|
|
private final int mIconTextColor;
|
|
private final int mIconBackgroundColor;
|
|
|
|
private LinearLayout mMessageBubble;
|
|
private ViewGroup mMessageTextAndInfoView;
|
|
private TextView mMessageTextView;
|
|
private TextView mStatusTextView;
|
|
private TextView mContactIconView;
|
|
|
|
public ConversationMessageView(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public ConversationMessageView(final Context context, final AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public ConversationMessageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
this(context, attrs, defStyleAttr, 0);
|
|
}
|
|
|
|
public ConversationMessageView(Context context, AttributeSet attrs, int defStyleAttr,
|
|
int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
|
final TypedArray a = context.obtainStyledAttributes(attrs,
|
|
R.styleable.ConversationMessageView);
|
|
|
|
mIncoming = a.getBoolean(R.styleable.ConversationMessageView_incoming, true);
|
|
mMessageText = a.getString(R.styleable.ConversationMessageView_messageText);
|
|
mTimestampText = a.getString(R.styleable.ConversationMessageView_timestampText);
|
|
mIconText = a.getString(R.styleable.ConversationMessageView_iconText);
|
|
mIconTextColor = a.getColor(R.styleable.ConversationMessageView_iconTextColor, 0);
|
|
mIconBackgroundColor = a.getColor(R.styleable.ConversationMessageView_iconBackgroundColor,
|
|
0);
|
|
|
|
LayoutInflater.from(context).inflate(R.layout.conversation_message_icon, this);
|
|
LayoutInflater.from(context).inflate(R.layout.conversation_message_content, this);
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
mMessageBubble = (LinearLayout) findViewById(R.id.message_content);
|
|
mMessageTextAndInfoView = (ViewGroup) findViewById(R.id.message_text_and_info);
|
|
mMessageTextView = (TextView) findViewById(R.id.message_text);
|
|
mStatusTextView = (TextView) findViewById(R.id.message_status);
|
|
mContactIconView = (TextView) findViewById(R.id.conversation_icon);
|
|
updateViewContent();
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
|
updateViewAppearance();
|
|
|
|
final int horizontalSpace = MeasureSpec.getSize(widthMeasureSpec);
|
|
final int iconSize = getResources()
|
|
.getDimensionPixelSize(R.dimen.conversation_message_contact_icon_size);
|
|
|
|
final int unspecifiedMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
|
final int iconMeasureSpec = MeasureSpec.makeMeasureSpec(iconSize, MeasureSpec.EXACTLY);
|
|
|
|
mContactIconView.measure(iconMeasureSpec, iconMeasureSpec);
|
|
|
|
final int arrowWidth =
|
|
getResources().getDimensionPixelSize(R.dimen.message_bubble_arrow_width);
|
|
|
|
// We need to subtract contact icon width twice from the horizontal space to get
|
|
// the max leftover space because we want the message bubble to extend no further than the
|
|
// starting position of the message bubble in the opposite direction.
|
|
final int maxLeftoverSpace = horizontalSpace - mContactIconView.getMeasuredWidth() * 2
|
|
- arrowWidth - getPaddingLeft() - getPaddingRight();
|
|
final int messageContentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(maxLeftoverSpace,
|
|
MeasureSpec.AT_MOST);
|
|
|
|
mMessageBubble.measure(messageContentWidthMeasureSpec, unspecifiedMeasureSpec);
|
|
|
|
final int maxHeight = Math.max(mContactIconView.getMeasuredHeight(),
|
|
mMessageBubble.getMeasuredHeight());
|
|
setMeasuredDimension(horizontalSpace, maxHeight + getPaddingBottom() + getPaddingTop());
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(final boolean changed, final int left, final int top, final int right,
|
|
final int bottom) {
|
|
final boolean isRtl = isLayoutRtl(this);
|
|
|
|
final int iconWidth = mContactIconView.getMeasuredWidth();
|
|
final int iconHeight = mContactIconView.getMeasuredHeight();
|
|
final int iconTop = getPaddingTop();
|
|
final int contentWidth = (right -left) - iconWidth - getPaddingLeft() - getPaddingRight();
|
|
final int contentHeight = mMessageBubble.getMeasuredHeight();
|
|
final int contentTop = iconTop;
|
|
|
|
final int iconLeft;
|
|
final int contentLeft;
|
|
|
|
if (mIncoming) {
|
|
if (isRtl) {
|
|
iconLeft = (right - left) - getPaddingRight() - iconWidth;
|
|
contentLeft = iconLeft - contentWidth;
|
|
} else {
|
|
iconLeft = getPaddingLeft();
|
|
contentLeft = iconLeft + iconWidth;
|
|
}
|
|
} else {
|
|
if (isRtl) {
|
|
iconLeft = getPaddingLeft();
|
|
contentLeft = iconLeft + iconWidth;
|
|
} else {
|
|
iconLeft = (right - left) - getPaddingRight() - iconWidth;
|
|
contentLeft = iconLeft - contentWidth;
|
|
}
|
|
}
|
|
|
|
mContactIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight);
|
|
|
|
mMessageBubble.layout(contentLeft, contentTop, contentLeft + contentWidth,
|
|
contentTop + contentHeight);
|
|
}
|
|
|
|
private static boolean isLayoutRtl(final View view) {
|
|
return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
|
|
}
|
|
|
|
private void updateViewContent() {
|
|
mMessageTextView.setText(mMessageText);
|
|
mStatusTextView.setText(mTimestampText);
|
|
mContactIconView.setText(mIconText);
|
|
|
|
mContactIconView.setTextColor(mIconTextColor);
|
|
final Drawable iconBase = getContext().getDrawable(R.drawable.conversation_message_icon);
|
|
mContactIconView
|
|
.setBackground(getTintedDrawable(getContext(), iconBase, mIconBackgroundColor));
|
|
}
|
|
|
|
private void updateViewAppearance() {
|
|
final Resources res = getResources();
|
|
|
|
final int arrowWidth = res.getDimensionPixelOffset(
|
|
R.dimen.message_bubble_arrow_width);
|
|
final int messageTextLeftRightPadding = res.getDimensionPixelOffset(
|
|
R.dimen.message_text_left_right_padding);
|
|
final int textTopPadding = res.getDimensionPixelOffset(
|
|
R.dimen.message_text_top_padding);
|
|
final int textBottomPadding = res.getDimensionPixelOffset(
|
|
R.dimen.message_text_bottom_padding);
|
|
|
|
final int textLeftPadding, textRightPadding;
|
|
|
|
if (mIncoming) {
|
|
textLeftPadding = messageTextLeftRightPadding + arrowWidth;
|
|
textRightPadding = messageTextLeftRightPadding;
|
|
} else {
|
|
textLeftPadding = messageTextLeftRightPadding;
|
|
textRightPadding = messageTextLeftRightPadding + arrowWidth;
|
|
}
|
|
|
|
// These values do not depend on whether the message includes attachments
|
|
final int gravity = mIncoming ? (Gravity.START | Gravity.CENTER_VERTICAL) :
|
|
(Gravity.END | Gravity.CENTER_VERTICAL);
|
|
final int messageTopPadding = res.getDimensionPixelSize(
|
|
R.dimen.message_padding_default);
|
|
final int metadataTopPadding = res.getDimensionPixelOffset(
|
|
R.dimen.message_metadata_top_padding);
|
|
|
|
// Update the message text/info views
|
|
final int bubbleDrawableResId = mIncoming ? R.drawable.msg_bubble_incoming
|
|
: R.drawable.msg_bubble_outgoing;
|
|
final int bubbleColorResId = mIncoming ? R.color.message_bubble_incoming
|
|
: R.color.message_bubble_outgoing;
|
|
final Context context = getContext();
|
|
|
|
final Drawable textBackgroundDrawable = getTintedDrawable(context,
|
|
context.getDrawable(bubbleDrawableResId),
|
|
context.getColor(bubbleColorResId));
|
|
mMessageTextAndInfoView.setBackground(textBackgroundDrawable);
|
|
|
|
if (isLayoutRtl(this)) {
|
|
// Need to switch right and left padding in RtL mode
|
|
mMessageTextAndInfoView.setPadding(textRightPadding,
|
|
textTopPadding + metadataTopPadding,
|
|
textLeftPadding, textBottomPadding);
|
|
} else {
|
|
mMessageTextAndInfoView.setPadding(textLeftPadding,
|
|
textTopPadding + metadataTopPadding,
|
|
textRightPadding, textBottomPadding);
|
|
}
|
|
|
|
// Update the message row and message bubble views
|
|
setPadding(getPaddingLeft(), messageTopPadding, getPaddingRight(), 0);
|
|
mMessageBubble.setGravity(gravity);
|
|
|
|
updateTextAppearance();
|
|
}
|
|
|
|
private void updateTextAppearance() {
|
|
final int messageColorResId = (mIncoming ? R.color.message_text_incoming
|
|
: R.color.message_text_outgoing);
|
|
final int timestampColorResId = mIncoming ? R.color.timestamp_text_incoming
|
|
: R.color.timestamp_text_outgoing;
|
|
final int messageColor = getContext().getColor(messageColorResId);
|
|
|
|
mMessageTextView.setTextColor(messageColor);
|
|
mMessageTextView.setLinkTextColor(messageColor);
|
|
mStatusTextView.setTextColor(timestampColorResId);
|
|
}
|
|
|
|
private static Drawable getTintedDrawable(final Context context, final Drawable drawable,
|
|
final int color) {
|
|
// For some reason occassionally drawables on JB has a null constant state
|
|
final Drawable.ConstantState constantStateDrawable = drawable.getConstantState();
|
|
final Drawable retDrawable = (constantStateDrawable != null)
|
|
? constantStateDrawable.newDrawable(context.getResources()).mutate()
|
|
: drawable;
|
|
retDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
|
return retDrawable;
|
|
}
|
|
}
|