diff --git a/res/drawable-hdpi/msg_bubble_incoming.9.png b/res/drawable-hdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..7fed0a806a0 Binary files /dev/null and b/res/drawable-hdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-hdpi/msg_bubble_outgoing.9.png b/res/drawable-hdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..8e7ccc0cc81 Binary files /dev/null and b/res/drawable-hdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..8e7ccc0cc81 Binary files /dev/null and b/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..7fed0a806a0 Binary files /dev/null and b/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..5ca9b14d641 Binary files /dev/null and b/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..4e7e4640376 Binary files /dev/null and b/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..52cb936a8ee Binary files /dev/null and b/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..0661fdba789 Binary files /dev/null and b/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..5d8fc747ee3 Binary files /dev/null and b/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..009c721919e Binary files /dev/null and b/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..8378db57676 Binary files /dev/null and b/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..f0d0d9ee5c9 Binary files /dev/null and b/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-mdpi/msg_bubble_incoming.9.png b/res/drawable-mdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..4e7e4640376 Binary files /dev/null and b/res/drawable-mdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-mdpi/msg_bubble_outgoing.9.png b/res/drawable-mdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..5ca9b14d641 Binary files /dev/null and b/res/drawable-mdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-xhdpi/msg_bubble_incoming.9.png b/res/drawable-xhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..0661fdba789 Binary files /dev/null and b/res/drawable-xhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-xhdpi/msg_bubble_outgoing.9.png b/res/drawable-xhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..52cb936a8ee Binary files /dev/null and b/res/drawable-xhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-xxhdpi/msg_bubble_incoming.9.png b/res/drawable-xxhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..ab693b3e7c1 Binary files /dev/null and b/res/drawable-xxhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-xxhdpi/msg_bubble_outgoing.9.png b/res/drawable-xxhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..5b2a2fffc0d Binary files /dev/null and b/res/drawable-xxhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable-xxxhdpi/msg_bubble_incoming.9.png b/res/drawable-xxxhdpi/msg_bubble_incoming.9.png new file mode 100644 index 00000000000..34130fc6d1b Binary files /dev/null and b/res/drawable-xxxhdpi/msg_bubble_incoming.9.png differ diff --git a/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png b/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png new file mode 100644 index 00000000000..cfd5734f106 Binary files /dev/null and b/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png differ diff --git a/res/drawable/conversation_message_icon.xml b/res/drawable/conversation_message_icon.xml new file mode 100644 index 00000000000..a1192ec5f1e --- /dev/null +++ b/res/drawable/conversation_message_icon.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/res/layout/conversation_message_content.xml b/res/layout/conversation_message_content.xml new file mode 100644 index 00000000000..7bc53e5074a --- /dev/null +++ b/res/layout/conversation_message_content.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + diff --git a/res/layout/conversation_message_icon.xml b/res/layout/conversation_message_icon.xml new file mode 100644 index 00000000000..4ffd2859cee --- /dev/null +++ b/res/layout/conversation_message_icon.xml @@ -0,0 +1,24 @@ + + + + diff --git a/res/layout/screen_zoom_preview_1.xml b/res/layout/screen_zoom_preview_1.xml index ca2ecca738c..62cd0933007 100644 --- a/res/layout/screen_zoom_preview_1.xml +++ b/res/layout/screen_zoom_preview_1.xml @@ -1,5 +1,5 @@ - - + android:background="@color/conversation_background" + android:padding="@dimen/conversation_message_list_padding" + android:orientation="vertical" > - + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index d94ff13d1be..c6dd2461314 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -106,6 +106,16 @@ + + + + + + + + + + diff --git a/res/values/colors.xml b/res/values/colors.xml index 90884c9b96b..f27b693bf5c 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -111,7 +111,20 @@ #4285F4 #3367D6 + #ffffffff + #ff323232 + #99323232 + #99ffffff + #689f38 + #ffffffff + #eeeeee + #689f38 + #ffffffff + #4285f4 + #ffffffff + + #fff - #CC000000 + #cc000000 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 14aac55209f..53dd515d324 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -258,6 +258,18 @@ 240dp 88dp + 10dp + 42dp + 32sp + 16sp + 12sp + 20dp + 9dp + 18dp + 14dp + 10dp + 12dp + 4dp 24dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 0e7ca629610..383506e6275 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6883,6 +6883,27 @@ density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] --> Custom (%d) + + A + + P + + Hi Pete! + + Hey, want to grab coffee and catch up today? + + Sounds great. I know of a good place not too far from here. + + Perfect! + + Tue 6:00PM + + Tue 6:01PM + + Tue 6:02PM + + Tue 6:03PM + See all diff --git a/src/com/android/settings/display/ConversationMessageView.java b/src/com/android/settings/display/ConversationMessageView.java new file mode 100644 index 00000000000..a0889c4ecf7 --- /dev/null +++ b/src/com/android/settings/display/ConversationMessageView.java @@ -0,0 +1,261 @@ +/* + * 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; + } +} diff --git a/src/com/android/settings/display/MessageBubbleBackground.java b/src/com/android/settings/display/MessageBubbleBackground.java new file mode 100644 index 00000000000..b2e1e9701c1 --- /dev/null +++ b/src/com/android/settings/display/MessageBubbleBackground.java @@ -0,0 +1,51 @@ +/* + * 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.util.AttributeSet; +import android.util.Log; +import android.widget.LinearLayout; + +import com.android.settings.R; + +public class MessageBubbleBackground extends LinearLayout { + private final int mSnapWidthPixels; + + public MessageBubbleBackground(Context context, AttributeSet attrs) { + super(context, attrs); + mSnapWidthPixels = context.getResources().getDimensionPixelSize( + R.dimen.conversation_bubble_width_snap); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + final int widthPadding = getPaddingLeft() + getPaddingRight(); + int bubbleWidth = getMeasuredWidth() - widthPadding; + final int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - widthPadding; + // Round up to next snapWidthPixels + bubbleWidth = Math.min(maxWidth, + (int) (Math.ceil(bubbleWidth / (float) mSnapWidthPixels) * mSnapWidthPixels)); + super.onMeasure( + MeasureSpec.makeMeasureSpec(bubbleWidth + widthPadding, MeasureSpec.EXACTLY), + heightMeasureSpec); + Log.w(this.getClass().getSimpleName(), + String.format("onMeasure called; width:%d, height:%d", this.getMeasuredWidth(), + this.getMeasuredHeight())); + } +}