blob: b054444fe57088e281d7111b75481c6a8bca649c [file] [log] [blame]
/*
* 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);
a.recycle();
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 unspecifiedMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int iconMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
mContactIconView.measure(iconMeasureSpec, iconMeasureSpec);
iconMeasureSpec = MeasureSpec.makeMeasureSpec(
Math.max(mContactIconView.getMeasuredWidth(), mContactIconView.getMeasuredHeight()),
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 occasionally 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;
}
}