supports dm conversation load more and refresh

This commit is contained in:
Mariotaku Lee 2017-02-13 16:05:31 +08:00
parent 3842396f36
commit 57b3ed3346
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
18 changed files with 560 additions and 126 deletions

View File

@ -21,6 +21,13 @@
package org.mariotaku.microblog.library.twitter.api;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.ConversationTimeline;
import org.mariotaku.microblog.library.twitter.model.NewDm;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.microblog.library.twitter.model.ResponseCode;
import org.mariotaku.microblog.library.twitter.model.UserEvents;
import org.mariotaku.microblog.library.twitter.model.UserInbox;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.KeyValue;
@ -29,12 +36,6 @@ import org.mariotaku.restfu.annotation.param.Path;
import org.mariotaku.restfu.annotation.param.Queries;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.ConversationTimeline;
import org.mariotaku.microblog.library.twitter.model.NewDm;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.microblog.library.twitter.model.ResponseCode;
import org.mariotaku.microblog.library.twitter.model.UserInbox;
@Queries(@KeyValue(key = "include_groups", value = "true"))
@ -54,6 +55,9 @@ public interface PrivateDirectMessagesResources extends PrivateResources {
@GET("/dm/user_inbox.json")
UserInbox getUserInbox(@Query Paging paging) throws MicroBlogException;
@GET("/dm/user_updates.json")
UserEvents getUserUpdates(@Query("cursor") String cursor) throws MicroBlogException;
@GET("/dm/conversation/{conversation_id}.json")
ConversationTimeline getUserInbox(@Path("conversation_id") String conversationId, @Query Paging paging) throws MicroBlogException;
}

View File

@ -23,6 +23,7 @@ package org.mariotaku.microblog.library.twitter.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.StringDef;
import com.bluelinelabs.logansquare.annotation.JsonField;
@ -94,8 +95,8 @@ public class DMResponse implements Parcelable {
return conversations;
}
public User getUser(long userId) {
return users.get(String.valueOf(userId));
public User getUser(String userId) {
return users.get(userId);
}
public Conversation getConversation(String conversationId) {
@ -129,6 +130,8 @@ public class DMResponse implements Parcelable {
Message participantsJoin;
@JsonField(name = "conversation_name_update")
Message conversationNameUpdate;
@JsonField(name = "conversation_read")
Message conversationRead;
public Message getJoinConversation() {
return joinConversation;
@ -154,6 +157,10 @@ public class DMResponse implements Parcelable {
return conversationNameUpdate;
}
public Message getConversationRead() {
return conversationRead;
}
@Override
public String toString() {
return "Entry{" +
@ -163,6 +170,7 @@ public class DMResponse implements Parcelable {
", participantsLeave=" + participantsLeave +
", participantsJoin=" + participantsJoin +
", conversationNameUpdate=" + conversationNameUpdate +
", conversationRead=" + conversationRead +
'}';
}
@ -455,11 +463,11 @@ public class DMResponse implements Parcelable {
@JsonField(name = "conversation_id")
String conversationId;
@JsonField(name = "last_read_event_id")
long lastReadEventId;
String lastReadEventId;
@JsonField(name = "max_entry_id")
long maxEntryId;
String maxEntryId;
@JsonField(name = "min_entry_id")
long minEntryId;
String minEntryId;
@JsonField(name = "notifications_disabled")
boolean notificationsDisabled;
@JsonField(name = "participants")
@ -467,7 +475,7 @@ public class DMResponse implements Parcelable {
@JsonField(name = "read_only")
boolean readOnly;
@JsonField(name = "sort_event_id")
long sortEventId;
String sortEventId;
@JsonField(name = "sort_timestamp")
long sortTimestamp;
@JsonField(name = "status")
@ -498,7 +506,7 @@ public class DMResponse implements Parcelable {
return sortTimestamp;
}
public long getSortEventId() {
public String getSortEventId() {
return sortEventId;
}
@ -514,15 +522,15 @@ public class DMResponse implements Parcelable {
return conversationId;
}
public long getLastReadEventId() {
public String getLastReadEventId() {
return lastReadEventId;
}
public long getMaxEntryId() {
public String getMaxEntryId() {
return maxEntryId;
}
public long getMinEntryId() {
public String getMinEntryId() {
return minEntryId;
}

View File

@ -0,0 +1,73 @@
/*
* 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.microblog.library.twitter.model;
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;
/**
* Created by mariotaku on 15/7/5.
*/
@ParcelablePlease
@JsonObject
public class UserEvents extends TwitterResponseObject implements Parcelable {
@JsonField(name = "user_events")
DMResponse userEvents;
public DMResponse getUserEvents() {
return userEvents;
}
@Override
public String toString() {
return "UserEvents{" +
"userEvents=" + userEvents +
"} " + super.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
UserEventsParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<UserEvents> CREATOR = new Creator<UserEvents>() {
public UserEvents createFromParcel(Parcel source) {
UserEvents target = new UserEvents();
UserEventsParcelablePlease.readFromParcel(target, source);
return target;
}
public UserEvents[] newArray(int size) {
return new UserEvents[size];
}
};
}

View File

@ -21,14 +21,19 @@
package org.mariotaku.microblog.library.twitter.model;
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;
/**
* Created by mariotaku on 15/7/5.
*/
@ParcelablePlease
@JsonObject
public class UserInbox extends TwitterResponseObject {
public class UserInbox extends TwitterResponseObject implements Parcelable {
@JsonField(name = "user_inbox")
DMResponse userInbox;
@ -37,5 +42,32 @@ public class UserInbox extends TwitterResponseObject {
return userInbox;
}
@Override
public String toString() {
return "UserInbox{" +
"userInbox=" + userInbox +
"} " + super.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
UserInboxParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<UserInbox> CREATOR = new Creator<UserInbox>() {
public UserInbox createFromParcel(Parcel source) {
UserInbox target = new UserInbox();
UserInboxParcelablePlease.readFromParcel(target, source);
return target;
}
public UserInbox[] newArray(int size) {
return new UserInbox[size];
}
};
}

View File

@ -33,7 +33,9 @@ import org.mariotaku.commons.objectcursor.LoganSquareCursorFieldConverter;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.twidere.model.message.MessageExtras;
import org.mariotaku.twidere.model.message.NameUpdatedExtras;
import org.mariotaku.twidere.model.message.StickerExtras;
import org.mariotaku.twidere.model.message.UserArrayExtras;
import org.mariotaku.twidere.model.util.MessageExtrasConverter;
import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
@ -167,12 +169,20 @@ public class ParcelableMessage {
static class InternalExtras {
@JsonField(name = "sticker")
StickerExtras sticker;
@JsonField(name = "name_updated")
NameUpdatedExtras nameUpdated;
@JsonField(name = "user_array")
UserArrayExtras userArray;
public static InternalExtras from(final MessageExtras extras) {
if (extras == null) return null;
InternalExtras result = new InternalExtras();
if (extras instanceof StickerExtras) {
result.sticker = (StickerExtras) extras;
} else if (extras instanceof NameUpdatedExtras) {
result.nameUpdated = (NameUpdatedExtras) extras;
} else if (extras instanceof UserArrayExtras) {
result.userArray = (UserArrayExtras) extras;
} else {
return null;
}
@ -182,6 +192,10 @@ public class ParcelableMessage {
public MessageExtras getExtras() {
if (sticker != null) {
return sticker;
} else if (nameUpdated != null) {
return nameUpdated;
} else if (userArray != null) {
return userArray;
}
return null;
}

View File

@ -25,12 +25,17 @@ import android.support.annotation.StringDef;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
import com.bluelinelabs.logansquare.annotation.OnPreJsonSerialize;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableNoThanks;
import org.mariotaku.commons.objectcursor.LoganSquareCursorFieldConverter;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.twidere.model.message.MessageExtras;
import org.mariotaku.twidere.model.message.conversation.ConversationExtras;
import org.mariotaku.twidere.model.message.conversation.TwitterOfficialConversationExtras;
import org.mariotaku.twidere.model.util.ConversationExtrasConverter;
import org.mariotaku.twidere.model.util.MessageExtrasConverter;
import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
@ -96,13 +101,16 @@ public class ParcelableMessageConversation {
@CursorField(value = Conversations.SPANS, converter = LoganSquareCursorFieldConverter.class)
public SpanItem[] spans;
@JsonField(name = "extras")
@CursorField(value = Conversations.EXTRAS, converter = MessageExtrasConverter.class)
public MessageExtras extras;
@CursorField(value = Conversations.MESSAGE_EXTRAS, converter = MessageExtrasConverter.class)
public MessageExtras message_extras;
@JsonField(name = "extras")
@ParcelableNoThanks
ParcelableMessage.InternalExtras internalExtras;
@JsonField(name = "conversation_extras_type")
@CursorField(Conversations.CONVERSATION_EXTRAS_TYPE)
public String conversation_extras_type;
@JsonField(name = "conversation_extras")
@CursorField(value = Conversations.CONVERSATION_EXTRAS, converter = ConversationExtrasConverter.class)
public ConversationExtras conversation_extras;
@JsonField(name = "participants")
@CursorField(value = Conversations.PARTICIPANTS, converter = LoganSquareCursorFieldConverter.class)
@ -124,6 +132,32 @@ public class ParcelableMessageConversation {
@CursorField(value = Conversations.REQUEST_CURSOR)
public String request_cursor;
@JsonField(name = "message_extras")
@ParcelableNoThanks
ParcelableMessage.InternalExtras internalMessageExtras;
@JsonField(name = "conversation_extras")
@ParcelableNoThanks
InternalExtras internalConversationExtras;
@OnPreJsonSerialize
void beforeJsonSerialize() {
internalMessageExtras = ParcelableMessage.InternalExtras.from(message_extras);
internalConversationExtras = InternalExtras.from(conversation_extras);
}
@OnJsonParseComplete
void onJsonParseComplete() {
if (internalMessageExtras != null) {
message_extras = internalMessageExtras.getExtras();
}
if (internalConversationExtras != null) {
conversation_extras = internalConversationExtras.getExtras();
}
}
@Override
public String toString() {
return "ParcelableMessageConversation{" +
@ -140,13 +174,16 @@ public class ParcelableMessageConversation {
", text_unescaped='" + text_unescaped + '\'' +
", media=" + Arrays.toString(media) +
", spans=" + Arrays.toString(spans) +
", extras=" + extras +
", internalExtras=" + internalExtras +
", message_extras=" + message_extras +
", conversation_extras=" + conversation_extras +
", participants=" + Arrays.toString(participants) +
", extras_type='" + conversation_extras_type + '\'' +
", sender_key=" + sender_key +
", recipient_key=" + recipient_key +
", is_outgoing=" + is_outgoing +
", request_cursor='" + request_cursor + '\'' +
", internalMessageExtras=" + internalMessageExtras +
", internalConversationExtras=" + internalConversationExtras +
'}';
}
@ -156,4 +193,34 @@ public class ParcelableMessageConversation {
String GROUP = "group";
}
@StringDef({ExtrasType.FANFOU, ExtrasType.TWITTER_OFFICIAL})
public @interface ExtrasType {
String FANFOU = "fanfou";
String TWITTER_OFFICIAL = "twitter_official";
}
@JsonObject
static class InternalExtras {
@JsonField(name = "twitter_official")
TwitterOfficialConversationExtras twitterOfficial;
public static InternalExtras from(final ConversationExtras extras) {
if (extras == null) return null;
InternalExtras result = new InternalExtras();
if (extras instanceof TwitterOfficialConversationExtras) {
result.twitterOfficial = (TwitterOfficialConversationExtras) extras;
} else {
return null;
}
return result;
}
public ConversationExtras getExtras() {
if (twitterOfficial != null) {
return twitterOfficial;
}
return null;
}
}
}

View File

@ -22,6 +22,8 @@
package org.mariotaku.twidere.model.message;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonObject;
@ -36,7 +38,8 @@ import java.io.IOException;
@JsonObject
public abstract class MessageExtras implements Parcelable {
public static MessageExtras parse(final String messageType, final String json) throws IOException {
public static MessageExtras parse(@NonNull final String messageType, @Nullable final String json) throws IOException {
if (json == null) return null;
switch (messageType) {
case MessageType.STICKER:
return LoganSquare.parse(json, StickerExtras.class);

View File

@ -19,37 +19,30 @@
* under the License.
*/
package org.mariotaku.microblog.library.twitter.model;
package org.mariotaku.twidere.model.message.conversation;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType;
import java.io.IOException;
/**
* Created by mariotaku on 16/3/1.
* Created by mariotaku on 2017/2/13.
*/
@JsonObject
public class UserEventsResponse extends TwitterResponseObject {
@JsonField(name = "user_events")
UserEvents userEvents;
public UserEvents getUserEvents() {
return userEvents;
}
@JsonObject
public static class UserEvents {
@JsonField(name = "cursor")
String cursor;
@JsonField(name = "last_seen_event_id")
long lastSeenEventId;
public String getCursor() {
return cursor;
}
public long getLastSeenEventId() {
return lastSeenEventId;
public abstract class ConversationExtras implements Parcelable {
public static ConversationExtras parse(@NonNull final String extrasType, @Nullable final String json) throws IOException {
if (json == null) return null;
switch (extrasType) {
case ExtrasType.TWITTER_OFFICIAL: {
return LoganSquare.parse(json, TwitterOfficialConversationExtras.class);
}
}
return null;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.conversation;
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.microblog.library.twitter.model.DMResponse;
/**
* Created by mariotaku on 2017/2/13.
*/
@ParcelablePlease
@JsonObject
public class TwitterOfficialConversationExtras extends ConversationExtras implements Parcelable {
@JsonField(name = "max_entry_id")
String maxEntryId;
@JsonField(name = "min_entry_id")
String minEntryId;
@JsonField(name = "status")
@DMResponse.Status
String status;
public String getMaxEntryId() {
return maxEntryId;
}
public void setMaxEntryId(final String maxEntryId) {
this.maxEntryId = maxEntryId;
}
public String getMinEntryId() {
return minEntryId;
}
public void setMinEntryId(final String minEntryId) {
this.minEntryId = minEntryId;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
TwitterOfficialConversationExtrasParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<TwitterOfficialConversationExtras> CREATOR = new Creator<TwitterOfficialConversationExtras>() {
public TwitterOfficialConversationExtras createFromParcel(Parcel source) {
TwitterOfficialConversationExtras target = new TwitterOfficialConversationExtras();
TwitterOfficialConversationExtrasParcelablePlease.readFromParcel(target, source);
return target;
}
public TwitterOfficialConversationExtras[] newArray(int size) {
return new TwitterOfficialConversationExtras[size];
}
};
}

View File

@ -0,0 +1,53 @@
/*
* 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.util;
import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.message.conversation.ConversationExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Messages;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
/**
* Created by mariotaku on 2017/2/9.
*/
public class ConversationExtrasConverter implements CursorFieldConverter<ConversationExtras> {
@Override
public ConversationExtras parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException {
final String extrasType = cursor.getString(cursor.getColumnIndex(Messages.Conversations.CONVERSATION_EXTRAS_TYPE));
if (TextUtils.isEmpty(extrasType)) return null;
return ConversationExtras.parse(extrasType, cursor.getString(columnIndex));
}
@Override
public void writeField(ContentValues values, ConversationExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return;
values.put(columnName, LoganSquare.serialize(object));
}
}

View File

@ -391,18 +391,20 @@ public interface TwidereDataStore {
String TEXT_UNESCAPED = "text_unescaped";
String MEDIA = "media";
String SPANS = "spans";
String EXTRAS = "extras";
String MESSAGE_EXTRAS = "message_extras";
String PARTICIPANTS = "participants";
String SENDER_KEY = "sender_key";
String RECIPIENT_KEY = "recipient_key";
String REQUEST_CURSOR = "request_cursor";
String IS_OUTGOING = "is_outgoing";
String CONVERSATION_EXTRAS = "conversation_extras";
String CONVERSATION_EXTRAS_TYPE = "conversation_extras_type";
String[] COLUMNS = ParcelableMessageConversationTableInfo.COLUMNS;
String[] TYPES = ParcelableMessageConversationTableInfo.TYPES;
String TABLE_NAME = "messages_conversations";
String CONTENT_PATH = "messages/conversations";
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
}

View File

@ -34,7 +34,7 @@ import static org.mariotaku.twidere.annotation.PreferenceType.STRING;
public interface Constants extends TwidereConstants {
String DATABASES_NAME = "twidere.sqlite";
int DATABASES_VERSION = 171;
int DATABASES_VERSION = 172;
int EXTRA_FEATURES_NOTICE_VERSION = 0;

View File

@ -33,6 +33,7 @@ import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableMessage.MessageType
import org.mariotaku.twidere.util.MediaLoadingHandler
import org.mariotaku.twidere.util.TwidereLinkify
import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder
import org.mariotaku.twidere.view.holder.message.MessageViewHolder
@ -50,6 +51,7 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
val linkHighlightingStyle: Int = preferences[linkHighlightOptionKey]
val nameFirst: Boolean = preferences[nameFirstKey]
val linkify: TwidereLinkify = TwidereLinkify(null)
val mediaLoadingHandler: MediaLoadingHandler = MediaLoadingHandler()
var messages: List<ParcelableMessage>? = null
private set
@ -140,5 +142,6 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
const val ITEM_TYPE_NOTICE_MESSAGE = 3
}
}

View File

@ -19,7 +19,7 @@ fun ParcelableMessageConversation.applyFrom(message: ParcelableMessage, details:
text_unescaped = message.text_unescaped
media = message.media
spans = message.spans
extras = message.extras
message_extras = message.extras
sender_key = message.sender_key
recipient_key = message.recipient_key
is_outgoing = message.is_outgoing
@ -43,7 +43,7 @@ fun ParcelableMessageConversation.getConversationName(context: Context,
fun ParcelableMessageConversation.getSummaryText(context: Context, manager: UserColorNameManager,
nameFirst: Boolean): CharSequence? {
return getSummaryText(context, manager, nameFirst, message_type, extras, sender_key,
return getSummaryText(context, manager, nameFirst, message_type, message_extras, sender_key,
text_unescaped, this)
}

View File

@ -92,8 +92,8 @@ object ParcelableMessageUtils {
this.is_outgoing = false
}
private fun ParcelableMessage.applyUsersEvent(accountKey: UserKey,
message: DMResponse.Entry.Message, users: Map<String, User>, @MessageType type: String) {
private fun ParcelableMessage.applyUsersEvent(accountKey: UserKey, message: DMResponse.Entry.Message,
users: Map<String, User>, @MessageType type: String) {
this.commonEntry(accountKey, message)
this.message_type = type
this.extras = UserArrayExtras().apply {
@ -105,8 +105,8 @@ object ParcelableMessageUtils {
this.is_outgoing = false
}
private fun ParcelableMessage.applyNameUpdatedEvent(accountKey: UserKey,
message: DMResponse.Entry.Message, users: Map<String, User>) {
private fun ParcelableMessage.applyNameUpdatedEvent(accountKey: UserKey, message: DMResponse.Entry.Message,
users: Map<String, User>) {
this.commonEntry(accountKey, message)
this.message_type = MessageType.CONVERSATION_NAME_UPDATE
this.extras = NameUpdatedExtras().apply {

View File

@ -19,6 +19,7 @@ import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
import org.mariotaku.twidere.model.event.GetMessagesTaskEvent
import org.mariotaku.twidere.model.message.conversation.TwitterOfficialConversationExtras
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.AccountUtils.getAccountDetails
import org.mariotaku.twidere.model.util.ParcelableMessageUtils
@ -74,27 +75,43 @@ class GetMessagesTask(
return getDefaultMessages(microBlog, details, param, index)
}
private fun getTwitterOfficialMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
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 cursor = param.cursors?.get(index)
val page = cursor?.substringAfter("page:").toInt(-1)
val inbox = microBlog.getUserInbox(Paging().apply {
count(60)
if (page >= 0) {
page(page)
}
}).userInbox
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>()
val conversationIds = inbox.conversations.keys
conversations.addLocalConversations(accountKey, conversationIds)
val messages = inbox.entries.mapNotNull {
ParcelableMessageUtils.fromEntry(accountKey, it, inbox.users)
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 inbox.conversations) {
for ((k, v) in respConversations) {
val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue
val participants = inbox.users.filterKeys { userId ->
val participants = respUsers.filterKeys { userId ->
v.participants.any { it.userId == userId }
}.values
val conversationType = when (v.type?.toUpperCase(Locale.US)) {
@ -105,6 +122,13 @@ class GetMessagesTask(
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)
}
@ -300,23 +324,27 @@ class GetMessagesTask(
override val sinceIds: Array<String?>?
get() {
val keys = accounts.map { account ->
when (account?.type) {
AccountType.FANFOU -> {
return@map null
}
}
return@map account?.key
}.toTypedArray()
val incomingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
keys, false)
defaultKeys, false)
val outgoingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
keys, true)
defaultKeys, true)
return incomingIds + outgoingIds
}
override val cursors: Array<String?>?
get() {
val cursors = arrayOfNulls<String>(defaultKeys.size)
val newestConversations = DataStoreUtils.getNewestConversations(context,
Messages.Conversations.CONTENT_URI, twitterOfficialKeys)
newestConversations.forEachIndexed { i, conversation ->
cursors[i] = conversation?.request_cursor
}
return cursors
}
override val hasSinceIds: Boolean = true
override val hasMaxIds: Boolean = false
override val hasCursors: Boolean = true
}
class LoadMoreTaskParam(
@ -324,22 +352,19 @@ class GetMessagesTask(
getAccountKeys: () -> Array<UserKey>
) : RefreshMessagesTaskParam(context, getAccountKeys) {
override val maxIds: Array<String?>?
get() {
val keys = accounts.map { account ->
when (account?.type) {
AccountType.FANFOU -> {
return@map null
}
}
return@map account?.key
}.toTypedArray()
val incomingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
keys, false)
val outgoingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
keys, true)
return incomingIds + outgoingIds
override val maxIds: Array<String?>? by lazy {
val incomingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, false)
val outgoingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true)
val oldestConversations = DataStoreUtils.getOldestConversations(context,
Messages.Conversations.CONTENT_URI, twitterOfficialKeys)
oldestConversations.forEachIndexed { i, conversation ->
val extras = conversation?.conversation_extras as? TwitterOfficialConversationExtras ?: return@forEachIndexed
incomingIds[i] = extras.maxEntryId
}
return@lazy incomingIds + outgoingIds
}
override val hasSinceIds: Boolean = false
override val hasMaxIds: Boolean = true
@ -359,6 +384,26 @@ class GetMessagesTask(
AccountUtils.getAllAccountDetails(AccountManager.get(context), accountKeys, false)
}
protected val defaultKeys: Array<UserKey?>by lazy {
return@lazy accounts.map { account ->
account ?: return@map null
if (account.isOfficial(context) || account.type == AccountType.FANFOU) {
return@map null
}
return@map account.key
}.toTypedArray()
}
protected val twitterOfficialKeys: Array<UserKey?> by lazy {
return@lazy accounts.map { account ->
account ?: return@map null
if (!account.isOfficial(context)) {
return@map null
}
return@map account.key
}.toTypedArray()
}
override final val accountKeys: Array<UserKey>
get() = getAccountKeys()

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.ArrayUtils
import org.apache.commons.lang3.StringUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.useCursor
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.sqliteqb.library.*
@ -173,11 +174,25 @@ object DataStoreUtils {
}
fun getOldestMessageIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>, outgoing: Boolean): Array<String?> {
if (accountKeys.all { it == null }) return kotlin.arrayOfNulls(accountKeys.size)
val having: Expression = Expression.equals(Messages.IS_OUTGOING, if (outgoing) 1 else 0)
return getStringFieldArray(context, uri, accountKeys, Messages.ACCOUNT_KEY, Messages.MESSAGE_ID,
OrderBy(SQLFunctions.MIN(Messages.LOCAL_TIMESTAMP)), having, null)
}
fun getOldestConversations(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<ParcelableMessageConversation?> {
if (accountKeys.all { it == null }) return kotlin.arrayOfNulls(accountKeys.size)
return getObjectFieldArray(context, uri, accountKeys, Conversations.ACCOUNT_KEY, Conversations.COLUMNS,
OrderBy(SQLFunctions.MIN(Messages.LOCAL_TIMESTAMP)), null, null,
::ParcelableMessageConversationCursorIndices, { arrayOfNulls<ParcelableMessageConversation>(it) })
}
fun getNewestConversations(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<ParcelableMessageConversation?> {
if (accountKeys.all { it == null }) return kotlin.arrayOfNulls(accountKeys.size)
return getObjectFieldArray(context, uri, accountKeys, Conversations.ACCOUNT_KEY, Conversations.COLUMNS,
OrderBy(SQLFunctions.MAX(Messages.LOCAL_TIMESTAMP)), null, null,
::ParcelableMessageConversationCursorIndices, { arrayOfNulls<ParcelableMessageConversation>(it) })
}
fun getNewestStatusSortIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
return getLongFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.SORT_ID,
@ -595,32 +610,58 @@ object DataStoreUtils {
return false
}
private fun getStringFieldArray(context: Context, uri: Uri,
keys: Array<UserKey?>, keyField: String,
valueField: String, sortExpression: OrderBy?,
extraHaving: Expression?, extraHavingArgs: Array<String>?): Array<String?> {
return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving,
extraHavingArgs, object : FieldArrayCreator<Array<String?>> {
private fun <T> getObjectFieldArray(context: Context, uri: Uri, keys: Array<UserKey?>,
keyField: String, valueFields: Array<String>, sortExpression: OrderBy?, extraHaving: Expression?,
extraHavingArgs: Array<String>?, createIndices: (Cursor) -> ObjectCursor.CursorIndices<T>,
createArray: (Int) -> Array<T?>): Array<T?> {
return getFieldsArray(context, uri, keys, keyField, valueFields, sortExpression,
extraHaving, extraHavingArgs, object : FieldArrayCreator<Array<T?>, ObjectCursor.CursorIndices<T>> {
override fun newArray(size: Int): Array<T?> {
return createArray(size)
}
override fun newIndex(cur: Cursor): ObjectCursor.CursorIndices<T> {
return createIndices(cur)
}
override fun assign(array: Array<T?>, arrayIdx: Int, cur: Cursor, colIdx: ObjectCursor.CursorIndices<T>) {
array[arrayIdx] = colIdx.newObject(cur)
}
})
}
private fun getStringFieldArray(context: Context, uri: Uri, keys: Array<UserKey?>,
keyField: String, valueField: String, sortExpression: OrderBy?, extraHaving: Expression?,
extraHavingArgs: Array<String>?): Array<String?> {
return getFieldsArray(context, uri, keys, keyField, arrayOf(valueField), sortExpression,
extraHaving, extraHavingArgs, object : FieldArrayCreator<Array<String?>, Int> {
override fun newArray(size: Int): Array<String?> {
return arrayOfNulls(size)
}
override fun newIndex(cur: Cursor): Int {
return cur.getColumnIndex(valueField)
}
override fun assign(array: Array<String?>, arrayIdx: Int, cur: Cursor, colIdx: Int) {
array[arrayIdx] = cur.getString(colIdx)
}
})
}
private fun getLongFieldArray(context: Context, uri: Uri,
keys: Array<UserKey?>, keyField: String,
valueField: String, sortExpression: OrderBy?,
extraHaving: Expression?, extraHavingArgs: Array<String>?): LongArray {
return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving,
extraHavingArgs, object : FieldArrayCreator<LongArray> {
private fun getLongFieldArray(context: Context, uri: Uri, keys: Array<UserKey?>,
keyField: String, valueField: String, sortExpression: OrderBy?, extraWhere: Expression?,
extraWhereArgs: Array<String>?): LongArray {
return getFieldsArray(context, uri, keys, keyField, arrayOf(valueField), sortExpression,
extraWhere, extraWhereArgs, object : FieldArrayCreator<LongArray, Int> {
override fun newArray(size: Int): LongArray {
return LongArray(size)
}
override fun newIndex(cur: Cursor): Int {
return cur.getColumnIndex(valueField)
}
override fun assign(array: LongArray, arrayIdx: Int, cur: Cursor, colIdx: Int) {
array[arrayIdx] = cur.getLong(colIdx)
}
@ -628,12 +669,12 @@ object DataStoreUtils {
}
@SuppressLint("Recycle")
private fun <T> getFieldArray(
private fun <T, I> getFieldsArray(
context: Context, uri: Uri,
keys: Array<UserKey?>, keyField: String,
valueField: String, sortExpression: OrderBy?,
valueFields: Array<String>, sortExpression: OrderBy?,
extraWhere: Expression?, extraWhereArgs: Array<String>?,
creator: FieldArrayCreator<T>
creator: FieldArrayCreator<T, I>
): T {
val resolver = context.contentResolver
val resultArray = creator.newArray(keys.size)
@ -646,7 +687,7 @@ object DataStoreUtils {
} else {
bindingArgs = nonNullKeys
}
val builder = SQLQueryBuilder.select(Columns(keyField, valueField))
val builder = SQLQueryBuilder.select(Columns(keyField, *valueFields))
builder.from(Table(tableName))
if (extraWhere != null) {
builder.where(extraWhere)
@ -659,13 +700,14 @@ object DataStoreUtils {
val rawUri = Uri.withAppendedPath(TwidereDataStore.CONTENT_URI_RAW_QUERY, builder.buildSQL())
resolver.query(rawUri, null, null, bindingArgs, null)?.useCursor { cur ->
cur.moveToFirst()
val colIdx = creator.newIndex(cur)
while (!cur.isAfterLast) {
val string = cur.getString(0)
if (string != null) {
val accountKey = UserKey.valueOf(string)
val idx = ArrayUtils.indexOf(keys, accountKey)
if (idx >= 0) {
creator.assign(resultArray, idx, cur, 1)
val keyString = cur.getString(cur.getColumnIndex(keyField))
if (keyString != null) {
val accountKey = UserKey.valueOf(keyString)
val arrayIdx = ArrayUtils.indexOf(keys, accountKey)
if (arrayIdx >= 0) {
creator.assign(resultArray, arrayIdx, cur, colIdx)
}
}
cur.moveToNext()
@ -737,10 +779,12 @@ object DataStoreUtils {
cursor.close()
}
internal interface FieldArrayCreator<T> {
internal interface FieldArrayCreator<T, I> {
fun newArray(size: Int): T
fun assign(array: T, arrayIdx: Int, cur: Cursor, colIdx: Int)
fun newIndex(cur: Cursor): I
fun assign(array: T, arrayIdx: Int, cur: Cursor, colIdx: I)
}
fun queryCount(context: Context, uri: Uri,

View File

@ -105,7 +105,7 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) :
} else {
mediaPreview.visibility = View.VISIBLE
mediaPreview.displayMedia(adapter.mediaLoader, message.media, message.account_key,
withCredentials = true)
withCredentials = true, loadingHandler = adapter.mediaLoadingHandler)
}
}