finishing streaming models

This commit is contained in:
Mariotaku Lee 2017-03-10 22:30:46 +08:00
parent 13dac670d2
commit 73f3aab3ee
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
16 changed files with 564 additions and 245 deletions

View File

@ -25,7 +25,6 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.StringDef; import android.support.annotation.StringDef;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.JsonMapper; import com.bluelinelabs.logansquare.JsonMapper;
import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.LoganSquare;
@ -248,34 +247,6 @@ public class Activity extends TwitterResponseObject implements TwitterResponse,
} }
} }
public static Activity fromMention(@NonNull String accountId, @NonNull Status status) {
final Activity activity = new Activity();
activity.maxPosition = activity.minPosition = status.getId();
activity.maxSortPosition = activity.minSortPosition = status.getSortId();
activity.createdAt = status.getCreatedAt();
if (TextUtils.equals(status.getInReplyToUserId(), accountId)) {
activity.action = Action.REPLY;
activity.targetStatuses = new Status[]{status};
//TODO set target statuses (in reply to status)
activity.targetObjectStatuses = new Status[0];
} else if (status.quotedStatus != null && TextUtils.equals(status.quotedStatus.user.getId(),
accountId)) {
activity.action = Action.QUOTE;
activity.targetStatuses = new Status[]{status};
activity.targetObjectStatuses = new Status[0];
} else {
activity.action = Action.MENTION;
activity.targetUsers = new User[0];
activity.targetObjectStatuses = new Status[]{status};
}
activity.sourcesSize = 1;
activity.sources = new User[]{status.getUser()};
return activity;
}
@StringDef({Action.FAVORITE, Action.FOLLOW, Action.MENTION, Action.REPLY, Action.RETWEET, @StringDef({Action.FAVORITE, Action.FOLLOW, Action.MENTION, Action.REPLY, Action.RETWEET,
Action.LIST_MEMBER_ADDED, Action.LIST_CREATED, Action.FAVORITED_RETWEET, Action.LIST_MEMBER_ADDED, Action.LIST_CREATED, Action.FAVORITED_RETWEET,
Action.RETWEETED_RETWEET, Action.QUOTE, Action.RETWEETED_MENTION, Action.RETWEETED_RETWEET, Action.QUOTE, Action.RETWEETED_MENTION,

View File

@ -21,6 +21,7 @@
package org.mariotaku.microblog.library.twitter.model; package org.mariotaku.microblog.library.twitter.model;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
/** /**
@ -28,4 +29,31 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
*/ */
@JsonObject @JsonObject
public class Warning { public class Warning {
@JsonField(name = "code")
String code;
@JsonField(name = "message")
String message;
@JsonField(name = "percent_full")
int percentFull;
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
public int getPercentFull() {
return percentFull;
}
@Override
public String toString() {
return "Warning{" +
"code='" + code + '\'' +
", message='" + message + '\'' +
", percentFull=" + percentFull +
'}';
}
} }

View File

@ -28,12 +28,17 @@ import org.apache.commons.cli.GnuParser
import org.apache.commons.cli.Options import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException import org.apache.commons.cli.ParseException
import org.mariotaku.microblog.library.twitter.TwitterUserStream import org.mariotaku.microblog.library.twitter.TwitterUserStream
import org.mariotaku.microblog.library.twitter.UserStreamCallback import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.microblog.library.twitter.model.* import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.extension.model.getCredentials import org.mariotaku.twidere.extension.model.getCredentials
import org.mariotaku.twidere.extension.model.newMicroBlogInstance import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.ActivityTitleSummaryMessage
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.util.dagger.DependencyHolder
import org.mariotaku.twidere.util.streaming.TimelineStreamCallback
/** /**
* Created by mariotaku on 2017/3/9. * Created by mariotaku on 2017/3/9.
@ -44,12 +49,16 @@ class UserStreamDumper(val context: Context) : DumperPlugin {
val parser = GnuParser() val parser = GnuParser()
val options = Options() val options = Options()
options.addRequiredOption("a", "account", true, "Account key") options.addRequiredOption("a", "account", true, "Account key")
options.addOption("t", "timeline", false, "Include timeline")
options.addOption("i", "interactions", false, "Include interactions")
val cmdLine = try { val cmdLine = try {
parser.parse(options, dumpContext.argsAsList.toTypedArray()) parser.parse(options, dumpContext.argsAsList.toTypedArray())
} catch (e: ParseException) { } catch (e: ParseException) {
throw DumpException(e.message) throw DumpException(e.message)
} }
val manager = DependencyHolder.get(context).userColorNameManager
val includeTimeline = cmdLine.hasOption("timeline")
val includeInteractions = cmdLine.hasOption("interactions")
val accountKey = UserKey.valueOf(cmdLine.getOptionValue("account")) val accountKey = UserKey.valueOf(cmdLine.getOptionValue("account"))
val am = AccountManager.get(context) val am = AccountManager.get(context)
val account = AccountUtils.findByAccountKey(am, accountKey) ?: return val account = AccountUtils.findByAccountKey(am, accountKey) ?: return
@ -58,17 +67,30 @@ class UserStreamDumper(val context: Context) : DumperPlugin {
cls = TwitterUserStream::class.java) cls = TwitterUserStream::class.java)
dumpContext.stdout.println("Beginning user stream...") dumpContext.stdout.println("Beginning user stream...")
dumpContext.stdout.flush() dumpContext.stdout.flush()
val callback = object : UserStreamCallback() { val callback = object : TimelineStreamCallback(accountKey.id) {
override fun onException(ex: Throwable): Boolean { override fun onException(ex: Throwable): Boolean {
ex.printStackTrace(dumpContext.stderr) ex.printStackTrace(dumpContext.stderr)
dumpContext.stderr.flush() dumpContext.stderr.flush()
return true return true
} }
override fun onStatus(status: Status): Boolean { override fun onHomeTimeline(status: Status) {
dumpContext.stdout.println("Status: @${status.user.screenName}: ${status.text.trim('\n')}") if (!includeTimeline && includeInteractions) return
dumpContext.stdout.println("Home: @${status.user.screenName}: ${status.text.trim('\n')}")
dumpContext.stdout.flush()
}
override fun onActivityAboutMe(activity: Activity) {
if (!includeInteractions && includeTimeline) return
val pActivity = ParcelableActivityUtils.fromActivity(activity, accountKey)
val message = ActivityTitleSummaryMessage.get(context, manager, pActivity, pActivity.sources, 0,
true, true)
if (message != null) {
dumpContext.stdout.println("Activity: ${message.title}: ${message.summary}")
} else {
dumpContext.stdout.println("Activity unsupported: ${activity.action}")
}
dumpContext.stdout.flush() dumpContext.stdout.flush()
return true
} }
override fun onDirectMessage(directMessage: DirectMessage): Boolean { override fun onDirectMessage(directMessage: DirectMessage): Boolean {
@ -76,38 +98,9 @@ class UserStreamDumper(val context: Context) : DumperPlugin {
dumpContext.stdout.flush() dumpContext.stdout.flush()
return true return true
} }
override fun onStatusDeleted(event: DeletionEvent): Boolean {
dumpContext.stdout.println("Status deleted: ${event.id}")
dumpContext.stdout.flush()
return true
}
override fun onDirectMessageDeleted(event: DeletionEvent): Boolean {
dumpContext.stdout.println("Message deleted: ${event.id}")
dumpContext.stdout.flush()
return true
}
override fun onFriendList(friendIds: Array<String>): Boolean {
dumpContext.stdout.println("Friends list: ${friendIds.size} in total")
dumpContext.stdout.flush()
return true
}
override fun onFavorite(source: User, target: User, targetStatus: Status): Boolean {
dumpContext.stdout.println("Favorited: @${source.screenName} -> ${targetStatus.text.trim('\n')}")
dumpContext.stdout.flush()
return true
}
override fun onUnhandledEvent(obj: TwitterStreamObject, json: String) {
dumpContext.stdout.println("Unhandled: ${obj.determine()} = $json")
dumpContext.stdout.flush()
}
} }
try { try {
userStream.getUserStream(callback) userStream.getUserStream("user", callback)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace(dumpContext.stderr) e.printStackTrace(dumpContext.stderr)
} }

View File

@ -19,9 +19,7 @@
package org.mariotaku.microblog.library.twitter; package org.mariotaku.microblog.library.twitter;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.restfu.annotation.method.GET; import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.callback.RawCallback;
/** /**
* Created by mariotaku on 15/5/26. * Created by mariotaku on 15/5/26.
@ -29,6 +27,6 @@ import org.mariotaku.restfu.callback.RawCallback;
public interface TwitterUserStream { public interface TwitterUserStream {
@GET("/user.json") @GET("/user.json")
void getUserStream(UserStreamCallback callback); void getUserStream(String with, UserStreamCallback callback);
} }

View File

@ -29,11 +29,13 @@ import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.DeletionEvent; import org.mariotaku.microblog.library.twitter.model.DeletionEvent;
import org.mariotaku.microblog.library.twitter.model.DirectMessage; import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.Status; import org.mariotaku.microblog.library.twitter.model.Status;
import org.mariotaku.microblog.library.twitter.model.StatusFavoriteEvent; import org.mariotaku.microblog.library.twitter.model.StatusTargetObjectEvent;
import org.mariotaku.microblog.library.twitter.model.StreamEvent;
import org.mariotaku.microblog.library.twitter.model.TwitterStreamObject; import org.mariotaku.microblog.library.twitter.model.TwitterStreamObject;
import org.mariotaku.microblog.library.twitter.model.TwitterStreamObject.Type; import org.mariotaku.microblog.library.twitter.model.TwitterStreamObject.Type;
import org.mariotaku.microblog.library.twitter.model.User; import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.microblog.library.twitter.model.UserList; import org.mariotaku.microblog.library.twitter.model.UserList;
import org.mariotaku.microblog.library.twitter.model.UserListTargetObjectEvent;
import org.mariotaku.microblog.library.twitter.model.Warning; import org.mariotaku.microblog.library.twitter.model.Warning;
import org.mariotaku.microblog.library.twitter.util.CRLFLineReader; import org.mariotaku.microblog.library.twitter.util.CRLFLineReader;
import org.mariotaku.restfu.callback.RawCallback; import org.mariotaku.restfu.callback.RawCallback;
@ -41,6 +43,7 @@ import org.mariotaku.restfu.http.HttpResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Date;
/** /**
* Created by mariotaku on 15/5/26. * Created by mariotaku on 15/5/26.
@ -83,8 +86,8 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
private boolean handleEvent(final TwitterStreamObject object, final String json) throws IOException { private boolean handleEvent(final TwitterStreamObject object, final String json) throws IOException {
switch (object.determine()) { switch (object.determine()) {
case Type.SENDER: { case Type.FRIENDS: {
break; return onFriendList(object.getFriends());
} }
case Type.STATUS: { case Type.STATUS: {
return onStatus(LoganSquare.parse(json, Status.class)); return onStatus(LoganSquare.parse(json, Status.class));
@ -104,56 +107,95 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
case Type.LIMIT: { case Type.LIMIT: {
return onTrackLimitationNotice(object.getLimit().getTrack()); return onTrackLimitationNotice(object.getLimit().getTrack());
} }
case Type.STALL_WARNING: case Type.STALL_WARNING: {
break; return onStallWarning(object.getWarning());
}
case Type.SCRUB_GEO: { case Type.SCRUB_GEO: {
TwitterStreamObject.ScrubGeo scrubGeo = object.getScrubGeo(); TwitterStreamObject.ScrubGeo scrubGeo = object.getScrubGeo();
return onScrubGeo(scrubGeo.getUserId(), scrubGeo.getUpToStatusId()); return onScrubGeo(scrubGeo.getUserId(), scrubGeo.getUpToStatusId());
} }
case Type.FRIENDS: {
return onFriendList(object.getFriends());
}
case Type.FAVORITE: { case Type.FAVORITE: {
StatusFavoriteEvent event = LoganSquare.parse(json, StatusFavoriteEvent.class); StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class);
return onFavorite(event.getSource(), event.getTarget(), event.getTargetObject()); return onFavorite(event.getCreatedAt(), event.getSource(), event.getTarget(),
event.getTargetObject());
} }
case Type.UNFAVORITE: { case Type.UNFAVORITE: {
StatusFavoriteEvent event = LoganSquare.parse(json, StatusFavoriteEvent.class); StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class);
return onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject()); return onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject());
} }
case Type.FOLLOW: case Type.QUOTED_TWEET: {
break; StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class);
case Type.UNFOLLOW: return onQuotedTweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
break; event.getTargetObject());
case Type.USER_LIST_MEMBER_ADDED: }
break; case Type.RETWEETED_RETWEET: {
case Type.USER_LIST_MEMBER_DELETED: StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class);
break; return onRetweetedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
case Type.USER_LIST_SUBSCRIBED: event.getTargetObject());
break; }
case Type.USER_LIST_UNSUBSCRIBED: case Type.FAVORITED_RETWEET: {
break; StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class);
case Type.USER_LIST_CREATED: return onFavoritedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
break; event.getTargetObject());
case Type.USER_LIST_UPDATED: }
break; case Type.FOLLOW: {
case Type.USER_LIST_DESTROYED: StreamEvent event = LoganSquare.parse(json, StreamEvent.class);
break; return onFollow(event.getCreatedAt(), event.getSource(), event.getTarget());
case Type.USER_UPDATE: }
break; case Type.UNFOLLOW: {
case Type.USER_DELETE: StreamEvent event = LoganSquare.parse(json, StreamEvent.class);
break; return onUnfollow(event.getCreatedAt(), event.getSource(), event.getTarget());
case Type.USER_SUSPEND: }
break; case Type.USER_LIST_MEMBER_ADDED: {
case Type.BLOCK: UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
break; return onUserListMemberAddition(event.getCreatedAt(), event.getSource(),
case Type.UNBLOCK: event.getTarget(), event.getTargetObject());
break; }
case Type.USER_LIST_MEMBER_DELETED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListMemberDeletion(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject());
}
case Type.USER_LIST_SUBSCRIBED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListSubscription(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject());
}
case Type.USER_LIST_UNSUBSCRIBED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListUnsubscription(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject());
}
case Type.USER_LIST_CREATED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListCreation(event.getCreatedAt(), event.getSource(),
event.getTargetObject());
}
case Type.USER_LIST_UPDATED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListUpdate(event.getCreatedAt(), event.getSource(),
event.getTargetObject());
}
case Type.USER_LIST_DESTROYED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class);
return onUserListDeletion(event.getCreatedAt(), event.getSource(),
event.getTargetObject());
}
case Type.USER_UPDATE: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class);
return onUserProfileUpdate(event.getCreatedAt(), event.getSource());
}
case Type.BLOCK: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class);
return onBlock(event.getCreatedAt(), event.getSource(), event.getTarget());
}
case Type.UNBLOCK: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class);
return onUnblock(event.getCreatedAt(), event.getSource(), event.getTarget());
}
case Type.DISCONNECTION: case Type.DISCONNECTION:
TwitterStreamObject.Disconnect disconnect = object.getDisconnect(); TwitterStreamObject.Disconnect disconnect = object.getDisconnect();
return onDisconnect(disconnect.getCode(), disconnect.getReason()); return onDisconnect(disconnect.getCode(), disconnect.getReason());
case Type.UNKNOWN:
break;
} }
return false; return false;
} }
@ -184,7 +226,7 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
return false; return false;
} }
protected boolean onBlock(User source, User blockedUser) { protected boolean onBlock(final Date createdAt, User source, User blockedUser) {
return false; return false;
} }
@ -200,11 +242,16 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
return false; return false;
} }
protected boolean onFavorite(@NonNull User source, @NonNull User target, @NonNull Status targetStatus) { protected boolean onFavorite(@NonNull Date createdAt, @NonNull User source, @NonNull User target,
@NonNull Status targetObject) {
return false; return false;
} }
protected boolean onFollow(User source, User followedUser) { protected boolean onFollow(@NonNull Date createdAt, @NonNull User source, @NonNull User target) {
return false;
}
protected boolean onUnfollow(@NonNull Date createdAt, @NonNull User source, @NonNull User target) {
return false; return false;
} }
@ -224,43 +271,65 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
return false; return false;
} }
protected boolean onUnblock(User source, User unblockedUser) { protected boolean onUnblock(final Date createdAt, User source, User unblockedUser) {
return false; return false;
} }
protected boolean onUnfavorite(User source, User target, Status targetStatus) { protected boolean onUnfavorite(@NonNull User source, @NonNull User target, @NonNull Status targetStatus) {
return false; return false;
} }
protected boolean onUserListCreation(User listOwner, UserList list) { protected boolean onUserListCreation(@NonNull Date createdAt, @NonNull User source,
@NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListDeletion(User listOwner, UserList list) { protected boolean onUserListDeletion(@NonNull Date createdAt, @NonNull User source,
@NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListMemberAddition(User addedMember, User listOwner, UserList list) { protected boolean onUserListMemberAddition(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListMemberDeletion(User deletedMember, User listOwner, UserList list) { protected boolean onUserListMemberDeletion(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListSubscription(User subscriber, User listOwner, UserList list) { protected boolean onUserListSubscription(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListUnsubscription(User subscriber, User listOwner, UserList list) { protected boolean onUserListUnsubscription(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserListUpdate(User listOwner, UserList list) { protected boolean onUserListUpdate(@NonNull Date createdAt, @NonNull User source, @NonNull UserList targetObject) {
return false; return false;
} }
protected boolean onUserProfileUpdate(User updatedUser) { protected boolean onUserProfileUpdate(@NonNull Date createdAt, @NonNull User updatedUser) {
return false;
}
protected boolean onQuotedTweet(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull Status targetObject) {
return false;
}
protected boolean onFavoritedRetweet(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull Status targetObject) {
return false;
}
protected boolean onRetweetedRetweet(@NonNull Date createdAt, @NonNull User source,
@NonNull User target, @NonNull Status targetObject) {
return false; return false;
} }

View File

@ -7,23 +7,18 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
* Created by mariotaku on 16/2/26. * Created by mariotaku on 16/2/26.
*/ */
@JsonObject @JsonObject
public class StatusFavoriteEvent { public class StatusTargetObjectEvent extends StreamEvent {
@JsonField(name = "source")
User source;
@JsonField(name = "target")
User target;
@JsonField(name = "target_object") @JsonField(name = "target_object")
Status targetObject; Status targetObject;
public User getSource() {
return source;
}
public User getTarget() {
return target;
}
public Status getTargetObject() { public Status getTargetObject() {
return targetObject; return targetObject;
} }
@Override
public String toString() {
return "StatusTargetObjectEvent{" +
"targetObject=" + targetObject +
"} " + super.toString();
}
} }

View File

@ -0,0 +1,61 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.microblog.library.twitter.model;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.microblog.library.twitter.util.TwitterDateConverter;
import java.util.Date;
/**
* Created by mariotaku on 16/2/26.
*/
@JsonObject
public class StreamEvent {
@JsonField(name = "created_at", typeConverter = TwitterDateConverter.class)
Date createdAt;
@JsonField(name = "source")
User source;
@JsonField(name = "target")
User target;
public Date getCreatedAt() {
return createdAt;
}
public User getSource() {
return source;
}
public User getTarget() {
return target;
}
@Override
public String toString() {
return "StreamEvent{" +
"createdAt=" + createdAt +
", source=" + source +
", target=" + target +
'}';
}
}

View File

@ -27,10 +27,10 @@ public class TwitterStreamObject {
@JsonField(name = "limit") @JsonField(name = "limit")
Limit limit; Limit limit;
@JsonField(name = "warning") @JsonField(name = "warning")
EmptyObject warning; Warning warning;
@JsonField(name = "scrub_geo") @JsonField(name = "scrub_geo")
ScrubGeo scrubGeo; ScrubGeo scrubGeo;
@JsonField(name = "friends") @JsonField(name = {"friends", "friends_str"})
String[] friends; String[] friends;
@Type @Type
@ -38,9 +38,7 @@ public class TwitterStreamObject {
// This code originally lived in AbstractStreamImplementation. // This code originally lived in AbstractStreamImplementation.
// I've moved it in here to expose it as a public encapsulation of // I've moved it in here to expose it as a public encapsulation of
// the object type determination logic. // the object type determination logic.
if (sender != null) { if (sender == null && text != null) {
return Type.SENDER;
} else if (text != null) {
return Type.STATUS; return Type.STATUS;
} else if (directMessage != null) { } else if (directMessage != null) {
return Type.DIRECT_MESSAGE; return Type.DIRECT_MESSAGE;
@ -82,14 +80,16 @@ public class TwitterStreamObject {
return Type.USER_LIST_DESTROYED; return Type.USER_LIST_DESTROYED;
case "user_update": case "user_update":
return Type.USER_UPDATE; return Type.USER_UPDATE;
case "user_delete":
return Type.USER_DELETE;
case "user_suspend":
return Type.USER_SUSPEND;
case "block": case "block":
return Type.BLOCK; return Type.BLOCK;
case "unblock": case "unblock":
return Type.UNBLOCK; return Type.UNBLOCK;
case "quoted_tweet":
return Type.QUOTED_TWEET;
case "favorited_retweet":
return Type.FAVORITED_RETWEET;
case "retweeted_retweet":
return Type.RETWEETED_RETWEET;
} }
} }
return Type.UNKNOWN; return Type.UNKNOWN;
@ -119,14 +119,18 @@ public class TwitterStreamObject {
return friends; return friends;
} }
@StringDef({Type.SENDER, Type.STATUS, Type.DIRECT_MESSAGE, Type.DELETE, Type.LIMIT, public Warning getWarning() {
return warning;
}
@StringDef({Type.STATUS, Type.DIRECT_MESSAGE, Type.DELETE, Type.LIMIT,
Type.STALL_WARNING, Type.SCRUB_GEO, Type.FRIENDS, Type.FAVORITE, Type.UNFAVORITE, Type.STALL_WARNING, Type.SCRUB_GEO, Type.FRIENDS, Type.FAVORITE, Type.UNFAVORITE,
Type.FOLLOW, Type.UNFOLLOW, Type.USER_LIST_MEMBER_ADDED, Type.USER_LIST_MEMBER_DELETED, Type.FOLLOW, Type.UNFOLLOW, Type.USER_LIST_MEMBER_ADDED, Type.USER_LIST_MEMBER_DELETED,
Type.USER_LIST_SUBSCRIBED, Type.USER_LIST_UNSUBSCRIBED, Type.USER_LIST_CREATED, Type.USER_LIST_SUBSCRIBED, Type.USER_LIST_UNSUBSCRIBED, Type.USER_LIST_CREATED,
Type.USER_LIST_UPDATED, Type.USER_LIST_DESTROYED, Type.USER_UPDATE, Type.USER_DELETE, Type.USER_LIST_UPDATED, Type.USER_LIST_DESTROYED, Type.USER_UPDATE, Type.BLOCK,
Type.USER_SUSPEND, Type.BLOCK, Type.UNBLOCK, Type.DISCONNECTION, Type.UNKNOWN}) Type.UNBLOCK, Type.DISCONNECTION, Type.QUOTED_TWEET, Type.FAVORITED_RETWEET,
Type.RETWEETED_RETWEET, Type.UNKNOWN})
public @interface Type { public @interface Type {
String SENDER = "sender";
String STATUS = "status"; String STATUS = "status";
String DIRECT_MESSAGE = "direct_message"; String DIRECT_MESSAGE = "direct_message";
String DELETE = "delete"; String DELETE = "delete";
@ -146,11 +150,12 @@ public class TwitterStreamObject {
String USER_LIST_UPDATED = "user_list_updated"; String USER_LIST_UPDATED = "user_list_updated";
String USER_LIST_DESTROYED = "user_list_destroyed"; String USER_LIST_DESTROYED = "user_list_destroyed";
String USER_UPDATE = "user_update"; String USER_UPDATE = "user_update";
String USER_DELETE = "user_delete";
String USER_SUSPEND = "user_suspend";
String BLOCK = "block"; String BLOCK = "block";
String UNBLOCK = "unblock"; String UNBLOCK = "unblock";
String DISCONNECTION = "disconnection"; String DISCONNECTION = "disconnection";
String QUOTED_TWEET = "quoted_tweet";
String FAVORITED_RETWEET = "favorited_retweet";
String RETWEETED_RETWEET = "retweeted_retweet";
String UNKNOWN = "unknown"; String UNKNOWN = "unknown";
} }

View File

@ -0,0 +1,43 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.microblog.library.twitter.model;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
/**
* Created by mariotaku on 16/2/26.
*/
@JsonObject
public class UserListTargetObjectEvent extends StreamEvent {
@JsonField(name = "target_object")
UserList targetObject;
public UserList getTargetObject() {
return targetObject;
}
@Override
public String toString() {
return "StatusTargetObjectEvent{" +
"targetObject=" + targetObject +
"} " + super.toString();
}
}

View File

@ -41,7 +41,6 @@ public class ActivityTitleSummaryMessage {
boolean shouldUseStarsForLikes, boolean shouldUseStarsForLikes,
boolean nameFirst) { boolean nameFirst) {
final Resources resources = context.getResources(); final Resources resources = context.getResources();
boolean byFriends = false;
switch (activity.action) { switch (activity.action) {
case Activity.Action.FOLLOW: { case Activity.Action.FOLLOW: {
int typeIcon = R.drawable.ic_activity_action_follow; int typeIcon = R.drawable.ic_activity_action_follow;

View File

@ -43,7 +43,7 @@ public class DefaultFeatures {
long twitterDirectMessageMediaLimit = 1; long twitterDirectMessageMediaLimit = 1;
@JsonField(name = "twitter_direct_message_max_participants") @JsonField(name = "twitter_direct_message_max_participants")
long twitterDirectMessageMaxParticipants = 20; long twitterDirectMessageMaxParticipants = 50;
public boolean isMediaLinkCountsInStatus() { public boolean isMediaLinkCountsInStatus() {
return mediaLinkCountsInStatus; return mediaLinkCountsInStatus;

View File

@ -0,0 +1,116 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.microblog.library.twitter.model
import java.util.*
/**
* Created by mariotaku on 2017/3/10.
*/
object InternalActivityCreator {
fun status(accountId: String, status: Status): Activity {
val activity = Activity()
activity.minPosition = status.getId()
activity.maxPosition = activity.minPosition
activity.minSortPosition = status.sortId
activity.maxSortPosition = activity.minSortPosition
activity.createdAt = status.getCreatedAt()
if (status.getInReplyToUserId() == accountId) {
activity.action = Activity.Action.REPLY
activity.targetStatuses = arrayOf(status)
//TODO set target statuses (in reply to status)
activity.targetObjectStatuses = arrayOfNulls<Status>(0)
} else if (status.quotedStatus?.user?.id == accountId) {
activity.action = Activity.Action.QUOTE
activity.targetStatuses = arrayOf(status)
activity.targetObjectStatuses = arrayOfNulls<Status>(0)
} else {
activity.action = Activity.Action.MENTION
activity.targetUsers = arrayOfNulls<User>(0)
activity.targetObjectStatuses = arrayOf(status)
}
activity.sourcesSize = 1
activity.sources = arrayOf(status.getUser())
return activity
}
fun retweet(status: Status): Activity {
val activity = Activity()
activity.initBasic(status.createdAt)
activity.action = Activity.Action.RETWEET
activity.sources = arrayOf(status.user)
activity.targetStatuses = arrayOf(status)
activity.targetObjectStatuses = arrayOf(status.retweetedStatus)
return activity
}
fun follow(createdAt: Date, source: User, target: User): Activity {
val activity = Activity()
activity.initBasic(createdAt)
activity.action = Activity.Action.FOLLOW
activity.sources = arrayOf(source)
activity.targetUsers = arrayOf(target)
return activity
}
fun targetStatus(action: String, createdAt: Date, source: User, target: Status): Activity {
val activity = Activity()
activity.initBasic(createdAt)
activity.action = action
activity.sources = arrayOf(source)
activity.targetStatuses = arrayOf(target)
return activity
}
fun targetObject(action: String, createdAt: Date, source: User, target: User, targetObject: UserList): Activity {
val activity = Activity()
activity.initBasic(createdAt)
activity.action = action
activity.sources = arrayOf(source)
activity.targetUsers = arrayOf(target)
activity.targetObjectUserLists = arrayOf(targetObject)
return activity
}
private fun Activity.initBasic(createdAt: Date) {
val timestamp = createdAt.time
minPosition = timestamp.toString()
maxPosition = timestamp.toString()
minSortPosition = timestamp
maxSortPosition = timestamp
this.createdAt = createdAt
}
}

View File

@ -57,7 +57,7 @@ object ParcelableActivityUtils {
return result return result
} }
fun fromActivity(activity: Activity, accountKey: UserKey, isGap: Boolean, fun fromActivity(activity: Activity, accountKey: UserKey, isGap: Boolean = false,
profileImageSize: String = "normal"): ParcelableActivity { profileImageSize: String = "normal"): ParcelableActivity {
val result = ParcelableActivity() val result = ParcelableActivity()
result.account_key = accountKey result.account_key = accountKey

View File

@ -5,7 +5,6 @@ import android.accounts.OnAccountsUpdateListener
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.ContentValues
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.IBinder import android.os.IBinder
@ -16,11 +15,6 @@ import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
import org.mariotaku.microblog.library.twitter.TwitterUserStream import org.mariotaku.microblog.library.twitter.TwitterUserStream
import org.mariotaku.microblog.library.twitter.UserStreamCallback import org.mariotaku.microblog.library.twitter.UserStreamCallback
import org.mariotaku.microblog.library.twitter.model.DeletionEvent
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.microblog.library.twitter.model.Warning
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.LOGTAG import org.mariotaku.twidere.TwidereConstants.LOGTAG
@ -31,11 +25,9 @@ import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.account.cred.OAuthCredentials import org.mariotaku.twidere.model.account.cred.OAuthCredentials
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.DebugLog import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.TwidereArrayUtils import org.mariotaku.twidere.util.TwidereArrayUtils
import java.io.IOException
class StreamingService : Service() { class StreamingService : Service() {
@ -105,7 +97,7 @@ class StreamingService : Service() {
callbacks.put(account.key, callback) callbacks.put(account.key, callback)
object : Thread() { object : Thread() {
override fun run() { override fun run() {
twitter.getUserStream(callback) twitter.getUserStream("user", callback)
Log.d(LOGTAG, String.format("Stream %s disconnected", account.key)) Log.d(LOGTAG, String.format("Stream %s disconnected", account.key))
callbacks.remove(account.key) callbacks.remove(account.key)
updateStreamState() updateStreamState()
@ -153,83 +145,6 @@ class StreamingService : Service() {
private var statusStreamStarted: Boolean = false private var statusStreamStarted: Boolean = false
private val mentionsStreamStarted: Boolean = false private val mentionsStreamStarted: Boolean = false
override fun onConnected() = true
override fun onBlock(source: User, blockedUser: User): Boolean {
val message = String.format("%s blocked %s", source.screenName, blockedUser.screenName)
Log.d(LOGTAG, message)
return true
}
override fun onDirectMessageDeleted(event: DeletionEvent): Boolean {
val where = Expression.equalsArgs(Messages.MESSAGE_ID).sql
val whereArgs = arrayOf(event.id)
context.contentResolver.delete(Messages.CONTENT_URI, where, whereArgs)
return true
}
override fun onStatusDeleted(event: DeletionEvent): Boolean {
val statusId = event.id
context.contentResolver.delete(Statuses.CONTENT_URI, Expression.equalsArgs(Statuses.STATUS_ID).sql,
arrayOf(statusId))
context.contentResolver.delete(Activities.AboutMe.CONTENT_URI, Expression.equalsArgs(Activities.STATUS_ID).sql,
arrayOf(statusId))
return true
}
override fun onFavorite(source: User, target: User, targetStatus: Status): Boolean {
val message = String.format("%s favorited %s's tweet: %s", source.screenName,
target.screenName, targetStatus.extendedText)
Log.d(LOGTAG, message)
return true
}
override fun onFollow(source: User, followedUser: User): Boolean {
val message = String
.format("%s followed %s", source.screenName, followedUser.screenName)
Log.d(LOGTAG, message)
return true
}
override fun onFriendList(friendIds: Array<String>): Boolean {
return true
}
override fun onScrubGeo(userId: String, upToStatusId: String): Boolean {
val resolver = context.contentResolver
val where = Expression.and(Expression.equalsArgs(Statuses.USER_KEY),
Expression.greaterEqualsArgs(Statuses.SORT_ID)).sql
val whereArgs = arrayOf(userId, upToStatusId)
val values = ContentValues()
values.putNull(Statuses.LOCATION)
resolver.update(Statuses.CONTENT_URI, values, where, whereArgs)
return true
}
override fun onStallWarning(warn: Warning): Boolean {
return true
}
@Throws(IOException::class)
override fun onStatus(status: Status): Boolean {
return true
}
override fun onUnblock(source: User, unblockedUser: User): Boolean {
val message = String.format("%s unblocked %s", source.screenName,
unblockedUser.screenName)
Log.d(LOGTAG, message)
return true
}
override fun onUnfavorite(source: User, target: User, targetStatus: Status): Boolean {
val message = String.format("%s unfavorited %s's tweet: %s", source.screenName,
target.screenName, targetStatus.extendedText)
Log.d(LOGTAG, message)
return true
}
} }
companion object { companion object {

View File

@ -23,10 +23,7 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Activity import org.mariotaku.microblog.library.twitter.model.*
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.extension.model.isOfficial
@ -73,7 +70,7 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
statuses = twitter.getMentionsTimeline(paging) statuses = twitter.getMentionsTimeline(paging)
} }
} }
statuses.mapTo(activities) { Activity.fromMention(details.key.id, it) } statuses.mapTo(activities) { InternalActivityCreator.status(details.key.id, it) }
return activities return activities
} }

View File

@ -0,0 +1,129 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util.streaming
import org.mariotaku.microblog.library.twitter.UserStreamCallback
import org.mariotaku.microblog.library.twitter.model.*
import java.util.*
/**
* Created by mariotaku on 2017/3/10.
*/
abstract class TimelineStreamCallback(val accountId: String) : UserStreamCallback() {
private val friends = mutableSetOf<String>()
override final fun onFriendList(friendIds: Array<String>): Boolean {
friends.addAll(friendIds)
return true
}
override final fun onStatus(status: Status): Boolean {
val userId = status.user.id
if (accountId == userId || userId in friends) {
onHomeTimeline(status)
}
if (status.inReplyToUserId == accountId) {
// Reply
onActivityAboutMe(InternalActivityCreator.status(accountId, status))
} else if (userId != accountId && status.retweetedStatus?.user?.id == accountId) {
// Retweet
onActivityAboutMe(InternalActivityCreator.retweet(status))
} else if (status.userMentionEntities?.find { it.id == accountId } != null) {
// Mention
onActivityAboutMe(InternalActivityCreator.status(accountId, status))
}
return true
}
override fun onFollow(createdAt: Date, source: User, target: User): Boolean {
if (source.id == accountId) {
friends.add(target.id)
} else if (target.id == accountId) {
// Dispatch follow activity
onActivityAboutMe(InternalActivityCreator.follow(createdAt, source, target))
}
return true
}
override fun onFavorite(createdAt: Date, source: User, target: User,
targetObject: Status): Boolean {
if (source.id == accountId) {
// Update my favorite status
} else if (target.id == accountId) {
// Dispatch favorite activity
onActivityAboutMe(InternalActivityCreator.targetStatus(Activity.Action.FAVORITE,
createdAt, source, targetObject))
}
return true
}
override fun onUnfollow(createdAt: Date, source: User, followedUser: User): Boolean {
if (source.id == accountId) {
friends.remove(followedUser.id)
}
return true
}
override fun onQuotedTweet(createdAt: Date, source: User, target: User, targetObject: Status): Boolean {
if (source.id == accountId) {
} else if (target.id == accountId) {
// Dispatch activity
onActivityAboutMe(InternalActivityCreator.targetStatus(Activity.Action.QUOTE,
createdAt, source, targetObject))
}
return true
}
override fun onFavoritedRetweet(createdAt: Date, source: User, target: User, targetObject: Status): Boolean {
if (source.id == accountId) {
} else if (target.id == accountId) {
// Dispatch activity
onActivityAboutMe(InternalActivityCreator.targetStatus(Activity.Action.FAVORITED_RETWEET,
createdAt, source, targetObject))
}
return true
}
override fun onRetweetedRetweet(createdAt: Date, source: User, target: User, targetObject: Status): Boolean {
if (source.id == accountId) {
} else if (target.id == accountId) {
// Dispatch activity
onActivityAboutMe(InternalActivityCreator.targetStatus(Activity.Action.RETWEETED_RETWEET,
createdAt, source, targetObject))
}
return true
}
override fun onUserListMemberAddition(createdAt: Date, source: User, target: User, targetObject: UserList): Boolean {
if (source.id == accountId) {
} else if (target.id == accountId) {
// Dispatch activity
onActivityAboutMe(InternalActivityCreator.targetObject(Activity.Action.LIST_MEMBER_ADDED,
createdAt, source, target, targetObject))
}
return true
}
protected abstract fun onHomeTimeline(status: Status)
protected abstract fun onActivityAboutMe(activity: Activity)
}