added profile image and name to message

This commit is contained in:
Mariotaku Lee 2017-02-13 17:56:08 +08:00
parent 57b3ed3346
commit b66b619354
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
10 changed files with 197 additions and 99 deletions

View File

@ -63,15 +63,21 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
when (viewType) {
ITEM_TYPE_TEXT_MESSAGE -> {
val view = inflater.inflate(MessageViewHolder.layoutResource, parent, false)
return MessageViewHolder(view, this)
val holder = MessageViewHolder(view, this)
holder.setup()
return holder
}
ITEM_TYPE_STICKER_MESSAGE -> {
val view = inflater.inflate(StickerMessageViewHolder.layoutResource, parent, false)
return StickerMessageViewHolder(view, this)
val holder = StickerMessageViewHolder(view, this)
holder.setup()
return holder
}
ITEM_TYPE_NOTICE_MESSAGE -> {
val view = inflater.inflate(NoticeSummaryEventViewHolder.layoutResource, parent, false)
return NoticeSummaryEventViewHolder(view, this)
val holder = NoticeSummaryEventViewHolder(view, this)
holder.setup()
return holder
}
}
throw UnsupportedOperationException()

View File

@ -77,60 +77,12 @@ class GetMessagesTask(
private fun getTwitterOfficialMessages(microBlog: MicroBlog, details: AccountDetails,
param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
if (param.conversationId != null) return GetMessagesData(emptyList(), emptyList())
val accountKey = details.key
val maxId = if (param.hasMaxIds) param.maxIds?.get(index) else null
val cursor = if (param.hasCursors) param.cursors?.get(index) else null
val response = if (cursor != null) {
microBlog.getUserUpdates(cursor).userEvents
val conversationId = param.conversationId
if (conversationId == null) {
return getTwitterOfficialUserInbox(microBlog, details, param, index)
} else {
microBlog.getUserInbox(Paging().apply {
if (maxId != null) {
maxId(maxId)
}
}).userInbox
}
val respConversations = response.conversations
val respEntries = response.entries
val respUsers = response.users
if (respConversations == null || respEntries == null || respUsers == null) {
return GetMessagesData(emptyList(), emptyList())
}
val conversations = hashMapOf<String, ParcelableMessageConversation>()
respConversations.keys.let {
conversations.addLocalConversations(accountKey, it)
}
val messages = respEntries.mapNotNull {
ParcelableMessageUtils.fromEntry(accountKey, it, respUsers)
}
val messagesMap = messages.groupBy(ParcelableMessage::conversation_id)
for ((k, v) in respConversations) {
val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue
val participants = respUsers.filterKeys { userId ->
v.participants.any { it.userId == userId }
}.values
val conversationType = when (v.type?.toUpperCase(Locale.US)) {
DMResponse.Conversation.Type.ONE_TO_ONE -> ConversationType.ONE_TO_ONE
DMResponse.Conversation.Type.GROUP_DM -> ConversationType.GROUP
else -> ConversationType.ONE_TO_ONE
}
val conversation = conversations.addConversation(k, details, message, participants,
conversationType)
conversation.conversation_name = v.name
conversation.request_cursor = response.cursor
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
this.minEntryId = v.minEntryId
this.maxEntryId = v.maxEntryId
this.status = v.status
}
}
return GetMessagesData(conversations.values, messages)
}
private fun getFanfouMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
@ -201,6 +153,66 @@ class GetMessagesTask(
return GetMessagesData(conversations.values, insertMessages)
}
private fun getTwitterOfficialConversation(microBlog: MicroBlog, details: AccountDetails,
conversationId: String, param: RefreshMessagesTaskParam, index: Int) {
}
private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
val accountKey = details.key
val maxId = if (param.hasMaxIds) param.maxIds?.get(index) else null
val cursor = if (param.hasCursors) param.cursors?.get(index) else null
val response = if (cursor != null) {
microBlog.getUserUpdates(cursor).userEvents
} else {
microBlog.getUserInbox(Paging().apply {
if (maxId != null) {
maxId(maxId)
}
}).userInbox
}
val respConversations = response.conversations
val respEntries = response.entries
val respUsers = response.users
if (respConversations == null || respEntries == null || respUsers == null) {
return GetMessagesData(emptyList(), emptyList())
}
val conversations = hashMapOf<String, ParcelableMessageConversation>()
respConversations.keys.let {
conversations.addLocalConversations(accountKey, it)
}
val messages = respEntries.mapNotNull {
ParcelableMessageUtils.fromEntry(accountKey, it, respUsers)
}
val messagesMap = messages.groupBy(ParcelableMessage::conversation_id)
for ((k, v) in respConversations) {
val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue
val participants = respUsers.filterKeys { userId ->
v.participants.any { it.userId == userId }
}.values
val conversationType = when (v.type?.toUpperCase(Locale.US)) {
DMResponse.Conversation.Type.ONE_TO_ONE -> ConversationType.ONE_TO_ONE
DMResponse.Conversation.Type.GROUP_DM -> ConversationType.GROUP
else -> ConversationType.ONE_TO_ONE
}
val conversation = conversations.addConversation(k, details, message, participants,
conversationType)
conversation.conversation_name = v.name
conversation.request_cursor = response.cursor
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
this.minEntryId = v.minEntryId
this.maxEntryId = v.maxEntryId
this.status = v.status
}
}
return GetMessagesData(conversations.values, messages)
}
private fun getFanfouConversations(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
val accountKey = details.key
val cursor = param.cursors?.get(index)

View File

@ -28,9 +28,11 @@ import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.MessagesConversationAdapter
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.view.ProfileImageView
/**
* Created by mariotaku on 2017/2/9.
@ -40,8 +42,20 @@ abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConvers
protected abstract val date: TextView
protected abstract val messageContent: View
protected open val profileImage: ProfileImageView? = null
protected open val nameTime: TextView? = null
open fun setup() {
val textSize = adapter.textSize
date.textSize = textSize * 0.9f
nameTime?.textSize = textSize * 0.8f
profileImage?.style = adapter.profileImageStyle
}
open fun display(message: ParcelableMessage, showDate: Boolean) {
val context = adapter.context
val manager = adapter.userColorNameManager
setMessageContentGravity(messageContent, message.is_outgoing)
if (showDate) {
date.visibility = View.VISIBLE
@ -50,6 +64,29 @@ abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConvers
} else {
date.visibility = View.GONE
}
val sender = message.sender_key?.let { adapter.findUser(it) }
nameTime?.apply {
val time = DateUtils.formatDateTime(context, message.timestamp, DateUtils.FORMAT_SHOW_TIME)
if (message.is_outgoing) {
this.text = time
} else if (sender != null) {
val senderName = manager.getDisplayName(sender, adapter.nameFirst)
this.text = context.getString(R.string.message_format_sender_time, senderName, time)
} else {
this.text = time
}
}
profileImage?.apply {
if (adapter.profileImageEnabled && sender != null && !message.is_outgoing) {
this.visibility = View.VISIBLE
adapter.mediaLoader.displayProfileImage(this, sender)
} else {
this.visibility = View.GONE
adapter.mediaLoader.cancelDisplayTask(this)
}
}
}
open fun setMessageContentGravity(view: View, outgoing: Boolean) {

View File

@ -20,7 +20,6 @@
package org.mariotaku.twidere.view.holder.message
import android.text.SpannableStringBuilder
import android.text.format.DateUtils
import android.view.View
import kotlinx.android.synthetic.main.list_item_message_conversation_text.view.*
import org.mariotaku.ktextension.empty
@ -29,10 +28,10 @@ import org.mariotaku.messagebubbleview.library.MessageBubbleView
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.MessagesConversationAdapter
import org.mariotaku.twidere.extension.model.applyTo
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.model.SpanItem
import org.mariotaku.twidere.view.FixedTextView
import org.mariotaku.twidere.view.ProfileImageView
/**
* Created by mariotaku on 2017/2/9.
@ -41,23 +40,25 @@ import org.mariotaku.twidere.view.FixedTextView
class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) : AbsMessageViewHolder(itemView, adapter) {
override val date: FixedTextView by lazy { itemView.date }
override val messageContent: MessageBubbleView by lazy { itemView.messageContent }
override val messageContent: View by lazy { itemView.messageContent }
override val profileImage: ProfileImageView by lazy { itemView.profileImage }
override val nameTime: FixedTextView by lazy { itemView.nameTime }
private val text by lazy { itemView.text }
private val time by lazy { itemView.time }
private val mediaPreview by lazy { itemView.mediaPreview }
private val messageBubble by lazy { itemView.messageBubble }
init {
override fun setup() {
super.setup()
val textSize = adapter.textSize
text.textSize = textSize
time.textSize = textSize * 0.8f
date.textSize = textSize * 0.9f
mediaPreview.style = adapter.mediaPreviewStyle
}
override fun display(message: ParcelableMessage, showDate: Boolean) {
super.display(message, showDate)
messageContent.setOutgoing(message.is_outgoing)
messageBubble.setOutgoing(message.is_outgoing)
// Loop through text and spans to found non-space char count
val hideText = run {
@ -97,9 +98,6 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) :
View.VISIBLE
}
time.text = DateUtils.formatDateTime(adapter.context, message.timestamp,
DateUtils.FORMAT_SHOW_TIME)
if (message.media.isNullOrEmpty()) {
mediaPreview.visibility = View.GONE
} else {
@ -107,13 +105,14 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) :
mediaPreview.displayMedia(adapter.mediaLoader, message.media, message.account_key,
withCredentials = true, loadingHandler = adapter.mediaLoadingHandler)
}
}
companion object {
const val layoutResource = R.layout.list_item_message_conversation_text
fun MessageBubbleView.setOutgoing(outgoing: Boolean) {
setCaretPosition(if (outgoing) MessageBubbleView.BOTTOM_END else MessageBubbleView.BOTTOM_START)
setCaretPosition(if (outgoing) MessageBubbleView.BOTTOM_END else MessageBubbleView.TOP_START)
}
}
}

View File

@ -27,6 +27,7 @@ import org.mariotaku.twidere.adapter.MessagesConversationAdapter
import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.model.message.StickerExtras
import org.mariotaku.twidere.view.FixedTextView
import org.mariotaku.twidere.view.ProfileImageView
/**
* Created by mariotaku on 2017/2/9.
@ -36,6 +37,8 @@ class StickerMessageViewHolder(itemView: View, adapter: MessagesConversationAdap
override val date: FixedTextView by lazy { itemView.date }
override val messageContent: RelativeLayout by lazy { itemView.messageContent }
override val profileImage: ProfileImageView by lazy { itemView.profileImage }
override val nameTime: FixedTextView by lazy { itemView.nameTime }
private val stickerIcon by lazy { itemView.stickerIcon }

View File

@ -66,13 +66,13 @@
android:layout_height="@dimen/element_size_normal"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="?selectableItemBackground"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:color="?android:textColorSecondary"
android:contentDescription="@string/add_image"
android:scaleType="centerInside"
android:src="@drawable/ic_action_gallery"
android:visibility="gone"/>
android:visibility="visible"/>
<org.mariotaku.twidere.view.IconActionView
android:id="@+id/sendMessage"
@ -80,7 +80,7 @@
android:layout_height="@dimen/element_size_normal"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="?selectableItemBackground"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:color="?android:textColorSecondary"
android:contentDescription="@string/action_send"

View File

@ -41,12 +41,35 @@
android:layout_height="wrap_content"
android:layout_below="@+id/date">
<org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profileImage"
android:layout_width="@dimen/profile_image_size_direct_message"
android:layout_height="@dimen/profile_image_size_direct_message"
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
tools:src="@drawable/ic_profile_image_twidere"/>
<ImageView
android:id="@+id/stickerIcon"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_alignParentTop="true"
android:layout_toEndOf="@+id/profileImage"
android:layout_toRightOf="@+id/profileImage"
android:contentDescription="@string/content_description_sticker"
android:scaleType="fitCenter"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/nameTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/stickerIcon"
android:layout_alignStart="@+id/stickerIcon"
android:layout_below="@+id/stickerIcon"
android:layout_margin="@dimen/element_spacing_normal"
android:textColor="?android:attr/textColorTertiary"
tools:text="12:00"/>
</RelativeLayout>
</RelativeLayout>

View File

@ -36,21 +36,37 @@
android:textColor="?android:textColorTertiary"
tools:text="Yesterday"/>
<org.mariotaku.messagebubbleview.library.MessageBubbleView
<RelativeLayout
android:id="@+id/messageContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profileImage"
android:layout_width="@dimen/profile_image_size_direct_message"
android:layout_height="@dimen/profile_image_size_direct_message"
android:layout_alignParentTop="true"
tools:src="@drawable/ic_profile_image_twidere"/>
<org.mariotaku.messagebubbleview.library.MessageBubbleView
android:id="@+id/messageBubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_toEndOf="@+id/profileImage"
android:layout_toRightOf="@+id/profileImage"
android:clickable="true"
app:bubbleColor="?messageBubbleColor"
app:caretHeight="8dp"
app:caretHeight="@dimen/element_spacing_normal"
app:caretPosition="topStart"
app:caretWidth="8dp"
app:caretWidth="@dimen/element_spacing_normal"
app:cornerRadius="2dp">
<RelativeLayout
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_below="@+id/mediaPreview"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.CardMediaContainer
android:id="@+id/mediaPreview"
@ -62,30 +78,27 @@
</org.mariotaku.twidere.view.CardMediaContainer>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/mediaPreview"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.TimelineContentTextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/element_spacing_small"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
tools:text="@string/sample_status_text"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorTertiary"
tools:text="12:00"/>
</LinearLayout>
</RelativeLayout>
</org.mariotaku.messagebubbleview.library.MessageBubbleView>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/nameTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/messageBubble"
android:layout_alignStart="@+id/messageBubble"
android:layout_below="@+id/messageBubble"
android:layout_margin="@dimen/element_spacing_normal"
android:textColor="?android:attr/textColorTertiary"
tools:text="12:00"/>
</RelativeLayout>
</LinearLayout>

View File

@ -97,6 +97,7 @@
<dimen name="preferred_tab_column_width">420dp</dimen>
<dimen name="profile_image_size_activity_small">32dp</dimen>
<dimen name="profile_image_size_direct_message">36dp</dimen>
<dimen name="settings_panel_margin_start_details">@dimen/element_size_normal</dimen>
<dimen name="settings_panel_width_entries">240dp</dimen>

View File

@ -688,6 +688,10 @@
</string>
<string name="message_format_participants_leave">
<xliff:g example="User" id="name">%1$s</xliff:g> left</string>
<string name="message_format_sender_time">
<xliff:g example="User" id="sender">%1$s</xliff:g> · <xliff:g
example="12:00" id="time">%2$s</xliff:g>
</string>
<string name="message_join_conversation">Joined conversation.</string>
<string name="message_permission_request_compose_location">Twidere needs location permission for adding location to tweets.</string>
<string name="message_permission_request_save_media">Twidere needs storage permission for saving media.</string>