diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/DMResponse.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/DMResponse.java index 41c8ea9a8..c34656edb 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/DMResponse.java +++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/DMResponse.java @@ -122,6 +122,10 @@ public class DMResponse implements Parcelable { Message joinConversation; @JsonField(name = "message") Message message; + @JsonField(name = "participants_leave") + Message participantsLeave; + @JsonField(name = "participants_join") + Message participantsJoin; public Message getJoinConversation() { return joinConversation; @@ -135,10 +139,21 @@ public class DMResponse implements Parcelable { return message; } + public Message getParticipantsLeave() { + return participantsLeave; + } + + public Message getParticipantsJoin() { + return participantsJoin; + } + @Override public String toString() { return "Entry{" + - "message=" + message + + "conversationCreate=" + conversationCreate + + ", joinConversation=" + joinConversation + + ", message=" + message + + ", participantsLeave=" + participantsLeave + '}'; } @@ -161,6 +176,9 @@ public class DMResponse implements Parcelable { @JsonField(name = "request_id") String requestId; + @JsonField(name = "sender_id") + String senderId; + @JsonField(name = "message_data") Data messageData; @@ -187,6 +205,10 @@ public class DMResponse implements Parcelable { return requestId; } + public String getSenderId() { + return senderId; + } + public Data getMessageData() { return messageData; } @@ -240,9 +262,9 @@ public class DMResponse implements Parcelable { @JsonField(name = "time") long time; @JsonField(name = "sender_id") - long senderId; + String senderId; @JsonField(name = "recipient_id") - long recipientId; + String recipientId; @JsonField(name = "text") String text; @JsonField(name = "entities") @@ -254,11 +276,11 @@ public class DMResponse implements Parcelable { return text; } - public long getRecipientId() { + public String getRecipientId() { return recipientId; } - public long getSenderId() { + public String getSenderId() { return senderId; } @@ -427,6 +449,26 @@ public class DMResponse implements Parcelable { @Type String type; + public String getType() { + return type; + } + + public String getStatus() { + return status; + } + + public long getSortTimestamp() { + return sortTimestamp; + } + + public long getSortEventId() { + return sortEventId; + } + + public boolean isReadOnly() { + return readOnly; + } + public Participant[] getParticipants() { return participants; } @@ -454,7 +496,7 @@ public class DMResponse implements Parcelable { @StringDef({Type.ONE_TO_ONE, Type.GROUP_DM}) @Retention(RetentionPolicy.SOURCE) public @interface Type { - String ONE_TO_ONE = "one_to_one", GROUP_DM = "group_dm"; + String ONE_TO_ONE = "ONE_TO_ONE", GROUP_DM = "GROUP_DM"; } @ParcelablePlease @@ -462,12 +504,18 @@ public class DMResponse implements Parcelable { public static class Participant implements Parcelable { @JsonField(name = "user_id") - long userId; + String userId; + @JsonField(name = "join_time") + long joinTime; - public long getUserId() { + public String getUserId() { return userId; } + public long getJoinTime() { + return joinTime; + } + @Override public int describeContents() { return 0; diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableMessage.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableMessage.java index 75db2c201..27b9b631c 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableMessage.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableMessage.java @@ -152,10 +152,12 @@ public class ParcelableMessage { @StringDef({MessageType.TEXT, MessageType.STICKER, MessageType.CONVERSATION_CREATE, - MessageType.JOIN_CONVERSATION}) + MessageType.JOIN_CONVERSATION, MessageType.PARTICIPANTS_LEAVE}) public @interface MessageType { String CONVERSATION_CREATE = "conversation_create"; String JOIN_CONVERSATION = "join_conversation"; + String PARTICIPANTS_LEAVE = "participants_leave"; + String PARTICIPANTS_JOIN = "participants_join"; String TEXT = "text"; String STICKER = "sticker"; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java index 1a9000c80..c15c421b5 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java @@ -40,6 +40,10 @@ public abstract class MessageExtras implements Parcelable { switch (messageType) { case MessageType.STICKER: return LoganSquare.parse(json, StickerExtras.class); + case MessageType.JOIN_CONVERSATION: + case MessageType.PARTICIPANTS_LEAVE: + case MessageType.PARTICIPANTS_JOIN: + return LoganSquare.parse(json, UserArrayExtras.class); } return null; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/UserArrayExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/UserArrayExtras.java new file mode 100644 index 000000000..2175a63fd --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/UserArrayExtras.java @@ -0,0 +1,72 @@ +/* + * Twidere - Twitter client for Android + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.mariotaku.twidere.model.message; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease; + +import org.mariotaku.twidere.model.ParcelableUser; + +/** + * Created by mariotaku on 2017/2/12. + */ + +@ParcelablePlease +@JsonObject +public class UserArrayExtras extends MessageExtras implements Parcelable { + @JsonField(name = "users") + ParcelableUser[] users; + + public ParcelableUser[] getUsers() { + return users; + } + + public void setUsers(final ParcelableUser[] users) { + this.users = users; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + UserArrayExtrasParcelablePlease.writeToParcel(this, dest, flags); + } + + public static final Creator CREATOR = new Creator() { + public UserArrayExtras createFromParcel(Parcel source) { + UserArrayExtras target = new UserArrayExtras(); + UserArrayExtrasParcelablePlease.readFromParcel(target, source); + return target; + } + + public UserArrayExtras[] newArray(int size) { + return new UserArrayExtras[size]; + } + }; +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesConversationAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesConversationAdapter.kt index 59e9b585f..490e937a7 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesConversationAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesConversationAdapter.kt @@ -29,14 +29,14 @@ import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter import org.mariotaku.twidere.annotation.PreviewStyle import org.mariotaku.twidere.constant.linkHighlightOptionKey import org.mariotaku.twidere.constant.mediaPreviewStyleKey +import org.mariotaku.twidere.constant.nameFirstKey import org.mariotaku.twidere.extension.model.timestamp -import org.mariotaku.twidere.model.ItemCounts -import org.mariotaku.twidere.model.ParcelableMessage +import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.ParcelableMessage.MessageType import org.mariotaku.twidere.util.TwidereLinkify import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder -import org.mariotaku.twidere.view.holder.message.ConversationCreateMessageViewHolder import org.mariotaku.twidere.view.holder.message.MessageViewHolder +import org.mariotaku.twidere.view.holder.message.NoticeSummaryEventViewHolder import org.mariotaku.twidere.view.holder.message.StickerMessageViewHolder import java.util.* @@ -48,13 +48,13 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter? = null - set(value) { - field = value - notifyDataSetChanged() - } + private set + var conversation: ParcelableMessageConversation? = null + private set override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) @@ -67,9 +67,10 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter { - val view = inflater.inflate(ConversationCreateMessageViewHolder.layoutResource, parent, false) - return ConversationCreateMessageViewHolder(view, this) + ITEM_TYPE_CONVERSATION_CREATE, ITEM_TYPE_JOIN_CONVERSATION, + ITEM_TYPE_PARTICIPANTS_LEAVE, ITEM_TYPE_PARTICIPANTS_JOIN -> { + val view = inflater.inflate(NoticeSummaryEventViewHolder.layoutResource, parent, false) + return NoticeSummaryEventViewHolder(view, this) } } throw UnsupportedOperationException() @@ -77,7 +78,8 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter { + ITEM_TYPE_TEXT_MESSAGE, ITEM_TYPE_STICKER_MESSAGE, ITEM_TYPE_CONVERSATION_CREATE, + ITEM_TYPE_JOIN_CONVERSATION, ITEM_TYPE_PARTICIPANTS_LEAVE, ITEM_TYPE_PARTICIPANTS_JOIN -> { val message = getMessage(position)!! // Display date for oldest item var showDate = true @@ -99,10 +101,6 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter { @@ -113,6 +111,15 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter { return ITEM_TYPE_CONVERSATION_CREATE } + MessageType.JOIN_CONVERSATION -> { + return ITEM_TYPE_JOIN_CONVERSATION + } + MessageType.PARTICIPANTS_LEAVE -> { + return ITEM_TYPE_PARTICIPANTS_LEAVE + } + MessageType.PARTICIPANTS_JOIN -> { + return ITEM_TYPE_PARTICIPANTS_JOIN + } else -> return ITEM_TYPE_TEXT_MESSAGE } } @@ -120,12 +127,30 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter?) { + this.conversation = conversation + this.messages = messages + notifyDataSetChanged() + } + companion object { private const val ITEM_START_MESSAGE = 0 const val ITEM_TYPE_TEXT_MESSAGE = 1 const val ITEM_TYPE_STICKER_MESSAGE = 2 const val ITEM_TYPE_CONVERSATION_CREATE = 3 + const val ITEM_TYPE_JOIN_CONVERSATION = 4 + const val ITEM_TYPE_PARTICIPANTS_LEAVE = 5 + const val ITEM_TYPE_PARTICIPANTS_JOIN = 6 } -} \ No newline at end of file +} + diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesEntriesAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesEntriesAdapter.kt index e79a13b1f..1ba7806b7 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesEntriesAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MessagesEntriesAdapter.kt @@ -4,8 +4,10 @@ import android.content.Context import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup +import org.mariotaku.kpreferences.get import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter +import org.mariotaku.twidere.constant.nameFirstKey import org.mariotaku.twidere.model.ItemCounts import org.mariotaku.twidere.model.ParcelableMessageConversation import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder @@ -31,6 +33,8 @@ class MessagesEntriesAdapter(context: Context) : LoadMoreSupportAdapter { - return context.getString(R.string.message_summary_type_sticker) - } - } - return text_unescaped +fun ParcelableMessageConversation.getSummaryText(context: Context, manager: UserColorNameManager, + nameFirst: Boolean): CharSequence? { + return getSummaryText(context, manager, nameFirst, message_type, extras, sender_key, + text_unescaped, this) } val ParcelableMessageConversation.user: ParcelableUser? diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMessageExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMessageExtensions.kt index 9b38782c9..6f8dd4d0c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMessageExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMessageExtensions.kt @@ -1,6 +1,14 @@ package org.mariotaku.twidere.extension.model +import android.content.Context +import org.mariotaku.twidere.R import org.mariotaku.twidere.model.ParcelableMessage +import org.mariotaku.twidere.model.ParcelableMessage.MessageType +import org.mariotaku.twidere.model.ParcelableMessageConversation +import org.mariotaku.twidere.model.UserKey +import org.mariotaku.twidere.model.message.MessageExtras +import org.mariotaku.twidere.model.message.UserArrayExtras +import org.mariotaku.twidere.util.UserColorNameManager /** * Created by mariotaku on 2017/2/9. @@ -8,3 +16,53 @@ import org.mariotaku.twidere.model.ParcelableMessage val ParcelableMessage.timestamp: Long get() = if (message_timestamp > 0) message_timestamp else local_timestamp + +fun ParcelableMessage.getSummaryText(context: Context, manager: UserColorNameManager, + conversation: ParcelableMessageConversation?, nameFirst: Boolean): CharSequence? { + return getSummaryText(context, manager, nameFirst, message_type, extras, sender_key, + text_unescaped, conversation) +} + +internal fun getSummaryText(context: Context, manager: UserColorNameManager, nameFirst: Boolean, + messageType: String?, extras: MessageExtras?, senderKey: UserKey?, text: String?, + conversation: ParcelableMessageConversation?): CharSequence? { + when (messageType) { + MessageType.STICKER -> { + return context.getString(R.string.message_summary_type_sticker) + } + MessageType.JOIN_CONVERSATION -> { + return context.getString(R.string.message_join_conversation) + } + MessageType.CONVERSATION_CREATE -> { + return context.getString(R.string.message_conversation_created) + } + MessageType.PARTICIPANTS_JOIN -> { + val users = (extras as UserArrayExtras).users + val sender = conversation?.participants?.firstOrNull { senderKey == it.key } + val res = context.resources + val joinName = if (users.size == 1) { + manager.getDisplayName(users[0], nameFirst) + } else { + res.getQuantityString(R.plurals.N_users, users.size, users.size) + } + if (sender != null) { + return res.getString(R.string.message_format_participants_join_added, + manager.getDisplayName(sender, nameFirst), joinName) + } else { + return res.getString(R.string.message_format_participants_join, joinName) + } + } + MessageType.PARTICIPANTS_LEAVE -> { + val users = (extras as UserArrayExtras).users + val res = context.resources + if (users.size == 1) { + val displayName = manager.getDisplayName(users[0], nameFirst) + return res.getString(R.string.message_format_participants_leave, displayName) + } else { + val usersName = res.getQuantityString(R.plurals.N_users, users.size, users.size) + return res.getString(R.string.message_format_participants_leave, usersName) + } + } + } + return text +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt index d9472b718..dda1b8275 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt @@ -1,5 +1,6 @@ package org.mariotaku.twidere.fragment +import android.content.Context import android.os.Bundle import android.support.v4.app.LoaderManager import android.support.v4.content.Loader @@ -17,9 +18,12 @@ import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID import org.mariotaku.twidere.loader.ObjectCursorLoader import org.mariotaku.twidere.model.ParcelableMessage +import org.mariotaku.twidere.model.ParcelableMessageConversation import org.mariotaku.twidere.model.ParcelableMessageCursorIndices import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.provider.TwidereDataStore.Messages +import org.mariotaku.twidere.util.DataStoreUtils +import java.util.concurrent.atomic.AtomicReference class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbacks?> { private lateinit var adapter: MessagesConversationAdapter @@ -40,22 +44,40 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac } override fun onCreateLoader(id: Int, args: Bundle?): Loader?> { - val loader = ObjectCursorLoader(context, ParcelableMessageCursorIndices::class.java) - loader.uri = Messages.CONTENT_URI - loader.projection = Messages.COLUMNS - loader.selection = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY), - Expression.equalsArgs(Messages.CONVERSATION_ID)).sql - loader.selectionArgs = arrayOf(accountKey.toString(), conversationId) - loader.sortOrder = OrderBy(Messages.SORT_ID, false).sql - return loader + return ConversationLoader(context, accountKey, conversationId) } override fun onLoaderReset(loader: Loader?>) { - adapter.messages = null + adapter.setData(null, null) } override fun onLoadFinished(loader: Loader?>, data: List?) { - adapter.messages = data + val conversation = (loader as? ConversationLoader)?.conversation + adapter.setData(conversation, data) + } + + internal class ConversationLoader( + context: Context, + val accountKey: UserKey, + val conversationId: String + ) : ObjectCursorLoader(context, ParcelableMessageCursorIndices::class.java) { + + private val atomicConversation = AtomicReference() + val conversation: ParcelableMessageConversation? get() = atomicConversation.get() + + init { + uri = Messages.CONTENT_URI + projection = Messages.COLUMNS + selection = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY), + Expression.equalsArgs(Messages.CONVERSATION_ID)).sql + selectionArgs = arrayOf(accountKey.toString(), conversationId) + sortOrder = OrderBy(Messages.SORT_ID, false).sql + } + + override fun onLoadInBackground(): MutableList { + atomicConversation.set(DataStoreUtils.findMessageConversation(context, accountKey, conversationId)) + return super.onLoadInBackground() + } } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableMessageUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableMessageUtils.kt index 7327202d7..ae06748b1 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableMessageUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableMessageUtils.kt @@ -3,12 +3,14 @@ package org.mariotaku.twidere.model.util import android.support.annotation.FloatRange import org.mariotaku.microblog.library.twitter.model.DMResponse import org.mariotaku.microblog.library.twitter.model.DirectMessage +import org.mariotaku.microblog.library.twitter.model.User import org.mariotaku.twidere.model.ParcelableMedia import org.mariotaku.twidere.model.ParcelableMessage import org.mariotaku.twidere.model.ParcelableMessage.MessageType import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.message.MessageExtras import org.mariotaku.twidere.model.message.StickerExtras +import org.mariotaku.twidere.model.message.UserArrayExtras import org.mariotaku.twidere.util.InternalTwitterContentUtils /** @@ -18,7 +20,8 @@ object ParcelableMessageUtils { fun fromMessage(accountKey: UserKey, message: DirectMessage, outgoing: Boolean, @FloatRange(from = 0.0, to = 1.0) sortIdAdj: Double = 0.0): ParcelableMessage { - val result = message(accountKey, message, sortIdAdj) + val result = ParcelableMessage() + result.applyMessage(accountKey, message, sortIdAdj) result.is_outgoing = outgoing if (outgoing) { result.conversation_id = outgoingConversationId(message.senderId, message.recipientId) @@ -28,7 +31,7 @@ object ParcelableMessageUtils { return result } - fun fromEntry(accountKey: UserKey, entry: DMResponse.Entry): ParcelableMessage? { + fun fromEntry(accountKey: UserKey, entry: DMResponse.Entry, users: Map): ParcelableMessage? { when { entry.message != null -> { return ParcelableMessage().apply { applyMessage(accountKey, entry.message) } @@ -36,6 +39,21 @@ object ParcelableMessageUtils { entry.conversationCreate != null -> { return ParcelableMessage().apply { applyConversationCreate(accountKey, entry.conversationCreate) } } + entry.joinConversation != null -> { + return ParcelableMessage().apply { + applyUsersEvent(accountKey, entry.joinConversation, users, MessageType.JOIN_CONVERSATION) + } + } + entry.participantsLeave != null -> { + return ParcelableMessage().apply { + applyUsersEvent(accountKey, entry.participantsLeave, users, MessageType.PARTICIPANTS_LEAVE) + } + } + entry.participantsJoin != null -> { + return ParcelableMessage().apply { + applyUsersEvent(accountKey, entry.participantsJoin, users, MessageType.PARTICIPANTS_JOIN) + } + } } return null } @@ -52,11 +70,6 @@ object ParcelableMessageUtils { this.commonEntry(accountKey, message) val data = message.messageData - - this.sender_key = UserKey(data.senderId.toString(), accountKey.host) - this.recipient_key = UserKey(data.recipientId.toString(), accountKey.host) - this.is_outgoing = this.sender_key == accountKey - val (type, extras, media) = typeAndExtras(data) val (text, spans) = InternalTwitterContentUtils.formatDirectMessageText(data) this.message_type = type @@ -72,37 +85,59 @@ object ParcelableMessageUtils { this.is_outgoing = false } + private fun ParcelableMessage.applyUsersEvent(accountKey: UserKey, + message: DMResponse.Entry.Message, users: Map, @MessageType type: String) { + this.commonEntry(accountKey, message) + this.message_type = type + this.extras = UserArrayExtras().apply { + this.users = message.participants.mapNotNull { + val user = users[it.userId] ?: return@mapNotNull null + ParcelableUserUtils.fromUser(user, accountKey) + }.toTypedArray() + } + this.is_outgoing = false + } + private fun ParcelableMessage.commonEntry(accountKey: UserKey, message: DMResponse.Entry.Message) { + val data = message.messageData + this.sender_key = run { + val senderId = data?.senderId ?: message.senderId ?: return@run null + return@run UserKey(senderId, accountKey.host) + } + this.recipient_key = run { + val recipientId = data?.recipientId ?: return@run null + return@run UserKey(recipientId, accountKey.host) + } this.account_key = accountKey this.id = message.id.toString() this.conversation_id = message.conversationId this.message_timestamp = message.time this.local_timestamp = this.message_timestamp this.sort_id = this.message_timestamp + + this.is_outgoing = this.sender_key == accountKey } - private fun message( + private fun ParcelableMessage.applyMessage( accountKey: UserKey, message: DirectMessage, @FloatRange(from = 0.0, to = 1.0) sortIdAdj: Double = 0.0 - ): ParcelableMessage { - val result = ParcelableMessage() - result.account_key = accountKey - result.id = message.id - result.sender_key = UserKeyUtils.fromUser(message.sender) - result.recipient_key = UserKeyUtils.fromUser(message.recipient) - result.message_timestamp = message.createdAt.time - result.local_timestamp = result.message_timestamp - result.sort_id = result.message_timestamp + (499 * sortIdAdj).toLong() + ) { + this.account_key = accountKey + this.id = message.id + this.sender_key = UserKeyUtils.fromUser(message.sender) + this.recipient_key = UserKeyUtils.fromUser(message.recipient) + this.message_timestamp = message.createdAt.time + this.local_timestamp = this.message_timestamp + this.sort_id = this.message_timestamp + (499 * sortIdAdj).toLong() val (type, extras) = typeAndExtras(message) val (text, spans) = InternalTwitterContentUtils.formatDirectMessageText(message) - result.message_type = type - result.extras = extras - result.text_unescaped = text - result.spans = spans - result.media = ParcelableMediaUtils.fromEntities(message) - return result + this.message_type = type + this.extras = extras + this.text_unescaped = text + this.spans = spans + this.media = ParcelableMediaUtils.fromEntities(message) } private fun typeAndExtras(message: DirectMessage): Pair { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt index 4b7ae459d..9357e3d50 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt @@ -7,13 +7,14 @@ import org.mariotaku.ktextension.toInt import org.mariotaku.ktextension.useCursor import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlogException +import org.mariotaku.microblog.library.twitter.model.DMResponse import org.mariotaku.microblog.library.twitter.model.Paging import org.mariotaku.microblog.library.twitter.model.User import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.extension.model.applyFrom import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.extension.model.newMicroBlogInstance -import org.mariotaku.twidere.extension.model.setFrom import org.mariotaku.twidere.extension.model.timestamp import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType @@ -27,6 +28,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Messages import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.content.ContentResolverUtils +import java.util.* /** * Created by mariotaku on 2017/2/8. @@ -86,14 +88,21 @@ class GetMessagesTask( val conversationIds = inbox.conversations.keys conversations.addLocalConversations(accountKey, conversationIds) - val messages = inbox.entries.mapNotNull { ParcelableMessageUtils.fromEntry(accountKey, it) } + val messages = inbox.entries.mapNotNull { + ParcelableMessageUtils.fromEntry(accountKey, it, inbox.users) + } val messagesMap = messages.groupBy(ParcelableMessage::conversation_id) for ((k, v) in inbox.conversations) { val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue val participants = inbox.users.filterKeys { userId -> - v.participants.any { it.userId.toString() == userId } + v.participants.any { it.userId == userId } }.values - conversations.addConversation(k, details, message, participants) + 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 + } + conversations.addConversation(k, details, message, participants, conversationType) } return GetMessagesData(conversations.values, messages) } @@ -256,22 +265,19 @@ class GetMessagesTask( conversationId: String, details: AccountDetails, message: ParcelableMessage, - users: Collection + users: Collection, + conversationType: String = ConversationType.ONE_TO_ONE ): ParcelableMessageConversation { val conversation = this[conversationId] ?: run { val obj = ParcelableMessageConversation() obj.id = conversationId - if (users.size == 2) { - obj.conversation_type = ConversationType.ONE_TO_ONE - } else { - obj.conversation_type = ConversationType.GROUP - } - obj.setFrom(message, details) + obj.conversation_type = conversationType + obj.applyFrom(message, details) this[conversationId] = obj return@run obj } if (message.timestamp > conversation.timestamp) { - conversation.setFrom(message, details) + conversation.applyFrom(message, details) } users.forEach { user -> conversation.addParticipant(details.key, user) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt index 41ed73733..ead25480e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt @@ -54,6 +54,7 @@ import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.ParcelableStatusUtils import org.mariotaku.twidere.provider.TwidereDataStore import org.mariotaku.twidere.provider.TwidereDataStore.* +import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations import org.mariotaku.twidere.util.content.ContentResolverUtils import java.io.IOException import java.util.* @@ -65,7 +66,7 @@ object DataStoreUtils { val STATUSES_URIS = arrayOf(Statuses.CONTENT_URI, CachedStatuses.CONTENT_URI) val CACHE_URIS = arrayOf(CachedUsers.CONTENT_URI, CachedStatuses.CONTENT_URI, CachedHashtags.CONTENT_URI, CachedTrends.Local.CONTENT_URI) - val MESSAGES_URIS = arrayOf(Messages.CONTENT_URI, Messages.Conversations.CONTENT_URI) + val MESSAGES_URIS = arrayOf(Messages.CONTENT_URI, Conversations.CONTENT_URI) val ACTIVITIES_URIS = arrayOf(Activities.AboutMe.CONTENT_URI) private val CONTENT_PROVIDER_URI_MATCHER = UriMatcher(UriMatcher.NO_MATCH) @@ -93,7 +94,7 @@ object DataStoreUtils { TABLE_ID_FILTERS_SUBSCRIPTIONS) CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Messages.CONTENT_PATH, TABLE_ID_MESSAGES) - CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Messages.Conversations.CONTENT_PATH, + CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Conversations.CONTENT_PATH, TABLE_ID_MESSAGES_CONVERSATIONS) CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedTrends.Local.CONTENT_PATH, TABLE_ID_TRENDS_LOCAL) @@ -227,7 +228,7 @@ object DataStoreUtils { } fun getActivitiesCount(context: Context, uri: Uri, - accountKey: UserKey): Int { + accountKey: UserKey): Int { val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql return queryCount(context, uri, where, arrayOf(accountKey.toString())) } @@ -281,11 +282,11 @@ object DataStoreUtils { } fun getStatusesCount(context: Context, - preferences: SharedPreferences, - uri: Uri, - extraArgs: Bundle?, compare: Long, - compareColumn: String, greaterThan: Boolean, - accountKeys: Array?): Int { + preferences: SharedPreferences, + uri: Uri, + extraArgs: Bundle?, compare: Long, + compareColumn: String, greaterThan: Boolean, + accountKeys: Array?): Int { val keys = accountKeys ?: getActivatedAccountKeys(context) val expressions = ArrayList() @@ -317,7 +318,7 @@ object DataStoreUtils { } fun getActivitiesCount(context: Context, uri: Uri, compare: Long, - compareColumn: String, greaterThan: Boolean, accountKeys: Array?): Int { + compareColumn: String, greaterThan: Boolean, accountKeys: Array?): Int { val keys = accountKeys ?: getActivatedAccountKeys(context) val selection = Expression.and( Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size), @@ -331,9 +332,9 @@ object DataStoreUtils { } fun getActivitiesCount(context: Context, uri: Uri, - extraWhere: Expression?, extraWhereArgs: Array?, - since: Long, sinceColumn: String, followingOnly: Boolean, - accountKeys: Array?): Int { + extraWhere: Expression?, extraWhereArgs: Array?, + since: Long, sinceColumn: String, followingOnly: Boolean, + accountKeys: Array?): Int { val keys = (accountKeys ?: getActivatedAccountKeys(context)).map { it.toString() }.toTypedArray() val expressions = ArrayList() expressions.add(Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size)) @@ -403,7 +404,7 @@ object DataStoreUtils { TABLE_ID_FILTERED_LINKS -> return Filters.Links.TABLE_NAME TABLE_ID_FILTERS_SUBSCRIPTIONS -> return Filters.Subscriptions.TABLE_NAME TABLE_ID_MESSAGES -> return Messages.TABLE_NAME - TABLE_ID_MESSAGES_CONVERSATIONS -> return Messages.Conversations.TABLE_NAME + TABLE_ID_MESSAGES_CONVERSATIONS -> return Conversations.TABLE_NAME TABLE_ID_TRENDS_LOCAL -> return CachedTrends.Local.TABLE_NAME TABLE_ID_TABS -> return Tabs.TABLE_NAME TABLE_ID_CACHED_STATUSES -> return CachedStatuses.TABLE_NAME @@ -595,9 +596,9 @@ object DataStoreUtils { } private fun getStringFieldArray(context: Context, uri: Uri, - keys: Array, keyField: String, - valueField: String, sortExpression: OrderBy?, - extraHaving: Expression?, extraHavingArgs: Array?): Array { + keys: Array, keyField: String, + valueField: String, sortExpression: OrderBy?, + extraHaving: Expression?, extraHavingArgs: Array?): Array { return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving, extraHavingArgs, object : FieldArrayCreator> { override fun newArray(size: Int): Array { @@ -611,9 +612,9 @@ object DataStoreUtils { } private fun getLongFieldArray(context: Context, uri: Uri, - keys: Array, keyField: String, - valueField: String, sortExpression: OrderBy?, - extraHaving: Expression?, extraHavingArgs: Array?): LongArray { + keys: Array, keyField: String, + valueField: String, sortExpression: OrderBy?, + extraHaving: Expression?, extraHavingArgs: Array?): LongArray { return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving, extraHavingArgs, object : FieldArrayCreator { override fun newArray(size: Int): LongArray { @@ -674,7 +675,7 @@ object DataStoreUtils { } fun deleteStatus(cr: ContentResolver, accountKey: UserKey, - statusId: String, status: ParcelableStatus?) { + statusId: String, status: ParcelableStatus?) { val host = accountKey.host val deleteWhere: String @@ -743,7 +744,7 @@ object DataStoreUtils { } fun queryCount(context: Context, uri: Uri, - selection: String?, selectionArgs: Array?): Int { + selection: String?, selectionArgs: Array?): Int { val resolver = context.contentResolver val projection = arrayOf(SQLFunctions.COUNT()) val cur = resolver.query(uri, projection, selection, selectionArgs, null) ?: return -1 @@ -758,7 +759,7 @@ object DataStoreUtils { } fun getInteractionsCount(context: Context, extraArgs: Bundle?, - accountIds: Array, since: Long, sinceColumn: String): Int { + accountIds: Array, since: Long, sinceColumn: String): Int { var extraWhere: Expression? = null var extraWhereArgs: Array? = null var followingOnly = false @@ -840,8 +841,8 @@ object DataStoreUtils { @WorkerThread fun findStatusInDatabases(context: Context, - accountKey: UserKey, - statusId: String): ParcelableStatus? { + accountKey: UserKey, + statusId: String): ParcelableStatus? { val resolver = context.contentResolver var status: ParcelableStatus? = null val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY), @@ -853,8 +854,6 @@ object DataStoreUtils { if (cur.count > 0 && cur.moveToFirst()) { status = ParcelableStatusCursorIndices.fromCursor(cur) } - } catch (e: IOException) { - // Ignore } finally { cur.close() } @@ -876,12 +875,25 @@ object DataStoreUtils { val resolver = context.contentResolver val status = ParcelableStatusUtils.fromStatus(result, accountKey, false) resolver.delete(CachedStatuses.CONTENT_URI, where, whereArgs) - try { - resolver.insert(CachedStatuses.CONTENT_URI, ParcelableStatusValuesCreator.create(status)) - } catch (e: IOException) { - // Ignore - } - + resolver.insert(CachedStatuses.CONTENT_URI, ParcelableStatusValuesCreator.create(status)) return status } + + @WorkerThread + fun findMessageConversation(context: Context, accountKey: UserKey, conversationId: String): ParcelableMessageConversation? { + val resolver = context.contentResolver + val where = Expression.and(Expression.equalsArgs(Conversations.ACCOUNT_KEY), + Expression.equalsArgs(Conversations.CONVERSATION_ID)).sql + val whereArgs = arrayOf(accountKey.toString(), conversationId) + val cur = resolver.query(Conversations.CONTENT_URI, Conversations.COLUMNS, where, whereArgs, null) ?: return null + try { + if (cur.count > 0 && cur.moveToFirst()) { + return ParcelableMessageConversationCursorIndices.fromCursor(cur) + } + } finally { + cur.close() + } + return null + } + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt index 30d6b7709..51a62dd5a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt @@ -59,7 +59,8 @@ class MessageEntryViewHolder(itemView: View, val adapter: MessagesEntriesAdapter this.name.name = name this.name.screenName = secondaryName this.name.updateText(adapter.bidiFormatter) - this.text.text = conversation.getSummaryText(itemView.context) + this.text.text = conversation.getSummaryText(itemView.context, adapter.userColorNameManager, + adapter.nameFirst) if (conversation.is_outgoing) { stateIndicator.visibility = View.VISIBLE stateIndicator.setImageResource(R.drawable.ic_activity_action_reply) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/ConversationCreateMessageViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt similarity index 82% rename from twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/ConversationCreateMessageViewHolder.kt rename to twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt index fd9029d88..22cf545a1 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/ConversationCreateMessageViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt @@ -23,10 +23,11 @@ import android.view.View import kotlinx.android.synthetic.main.list_item_message_conversation_notice.view.* import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.MessagesConversationAdapter +import org.mariotaku.twidere.extension.model.getSummaryText import org.mariotaku.twidere.model.ParcelableMessage import org.mariotaku.twidere.view.FixedTextView -class ConversationCreateMessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) : AbsMessageViewHolder(itemView, adapter) { +class NoticeSummaryEventViewHolder(itemView: View, adapter: MessagesConversationAdapter) : AbsMessageViewHolder(itemView, adapter) { override val messageContent: View = itemView override val date: FixedTextView by lazy { itemView.date } @@ -34,7 +35,8 @@ class ConversationCreateMessageViewHolder(itemView: View, adapter: MessagesConve override fun display(message: ParcelableMessage, showDate: Boolean) { super.display(message, showDate) - text.setText(R.string.message_conversation_created) + text.text = message.getSummaryText(adapter.context, adapter.userColorNameManager, + adapter.conversation, adapter.nameFirst) } override fun setMessageContentGravity(view: View, outgoing: Boolean) { @@ -44,5 +46,4 @@ class ConversationCreateMessageViewHolder(itemView: View, adapter: MessagesConve companion object { const val layoutResource = R.layout.list_item_message_conversation_notice } - } \ No newline at end of file diff --git a/twidere/src/main/res/layout/list_item_message_conversation_notice.xml b/twidere/src/main/res/layout/list_item_message_conversation_notice.xml index b22e8f71f..b8973a85d 100644 --- a/twidere/src/main/res/layout/list_item_message_conversation_notice.xml +++ b/twidere/src/main/res/layout/list_item_message_conversation_notice.xml @@ -40,5 +40,6 @@ android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:padding="@dimen/element_spacing_normal" tools:text="Notice"/> \ No newline at end of file diff --git a/twidere/src/main/res/values/strings.xml b/twidere/src/main/res/values/strings.xml index 5f055b08a..d05dbeacc 100644 --- a/twidere/src/main/res/values/strings.xml +++ b/twidere/src/main/res/values/strings.xml @@ -669,6 +669,16 @@ Direct message deleted. Direct message sent. Some account data are corrupted, Twidere will remove those accounts to prevent crash. + + %1$s joined + + %1$s added %2$s + + + %1$s left + Joined conversation. Twidere needs location permission for adding location to tweets. Twidere needs storage permission for saving media. Twidere needs storage permission for sharing media to some apps.