parent
4610e2a3f7
commit
13dac670d2
|
@ -41,7 +41,8 @@ import java.util.List;
|
|||
@ParcelablePlease
|
||||
public class UserKey implements Comparable<UserKey>, Parcelable {
|
||||
|
||||
public static final UserKey SELF_REFERENCE = new UserKey("#self#", "#self#");
|
||||
public static final UserKey SELF = new UserKey("#self#", "#self#");
|
||||
public static final UserKey INVALID = new UserKey("#invalid#", "#invalid#");
|
||||
|
||||
public static final Creator<UserKey> CREATOR = new Creator<UserKey>() {
|
||||
@Override
|
||||
|
@ -75,8 +76,12 @@ public class UserKey implements Comparable<UserKey>, Parcelable {
|
|||
|
||||
}
|
||||
|
||||
public boolean isSelfReference() {
|
||||
return equals(SELF_REFERENCE);
|
||||
public boolean isSelf() {
|
||||
return equals(SELF);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return !equals(INVALID);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -31,7 +31,7 @@ import com.squareup.leakcanary.RefWatcher;
|
|||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.util.net.NoIntercept;
|
||||
import org.mariotaku.twidere.util.stetho.AccountsDumper;
|
||||
import org.mariotaku.twidere.util.stetho.RawStreamDumper;
|
||||
import org.mariotaku.twidere.util.stetho.UserStreamDumper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -70,7 +70,7 @@ public class DebugModeUtils {
|
|||
public Iterable<DumperPlugin> get() {
|
||||
return new Stetho.DefaultDumperPluginsBuilder(application)
|
||||
.provide(new AccountsDumper(application))
|
||||
.provide(new RawStreamDumper(application))
|
||||
.provide(new UserStreamDumper(application))
|
||||
.finish();
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* 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.stetho
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import com.facebook.stetho.dumpapp.DumpException
|
||||
import com.facebook.stetho.dumpapp.DumperContext
|
||||
import com.facebook.stetho.dumpapp.DumperPlugin
|
||||
import org.apache.commons.cli.GnuParser
|
||||
import org.apache.commons.cli.Options
|
||||
import org.apache.commons.cli.ParseException
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUserStream
|
||||
import org.mariotaku.restfu.callback.RawCallback
|
||||
import org.mariotaku.restfu.http.HttpResponse
|
||||
import org.mariotaku.twidere.extension.model.getCredentials
|
||||
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/3/9.
|
||||
*/
|
||||
class RawStreamDumper(val context: Context) : DumperPlugin {
|
||||
|
||||
override fun dump(dumpContext: DumperContext) {
|
||||
val parser = GnuParser()
|
||||
val options = Options()
|
||||
options.addRequiredOption("a", "account", true, "Account key")
|
||||
val cmdLine = try {
|
||||
parser.parse(options, dumpContext.argsAsList.toTypedArray())
|
||||
} catch (e: ParseException) {
|
||||
throw DumpException(e.message)
|
||||
}
|
||||
|
||||
val accountKey = UserKey.valueOf(cmdLine.getOptionValue("account"))
|
||||
val am = AccountManager.get(context)
|
||||
val account = AccountUtils.findByAccountKey(am, accountKey) ?: return
|
||||
val credentials = account.getCredentials(am)
|
||||
val userStream = credentials.newMicroBlogInstance(context, account.type,
|
||||
cls = TwitterUserStream::class.java)
|
||||
userStream.getUserStreamRaw(object : RawCallback<MicroBlogException> {
|
||||
override fun result(result: HttpResponse) {
|
||||
dumpContext.stdout.println("Response: ${result.status}")
|
||||
dumpContext.stdout.println("Headers:")
|
||||
result.headers.toList().forEach {
|
||||
dumpContext.stdout.println("${it.first}: ${it.second}")
|
||||
}
|
||||
dumpContext.stdout.println()
|
||||
result.body.writeTo(dumpContext.stdout)
|
||||
}
|
||||
|
||||
override fun error(exception: MicroBlogException) {
|
||||
exception.printStackTrace(dumpContext.stderr)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun getName() = "raw_stream"
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.stetho
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import com.facebook.stetho.dumpapp.DumpException
|
||||
import com.facebook.stetho.dumpapp.DumperContext
|
||||
import com.facebook.stetho.dumpapp.DumperPlugin
|
||||
import org.apache.commons.cli.GnuParser
|
||||
import org.apache.commons.cli.Options
|
||||
import org.apache.commons.cli.ParseException
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUserStream
|
||||
import org.mariotaku.microblog.library.twitter.UserStreamCallback
|
||||
import org.mariotaku.microblog.library.twitter.model.*
|
||||
import org.mariotaku.twidere.extension.model.getCredentials
|
||||
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/3/9.
|
||||
*/
|
||||
class UserStreamDumper(val context: Context) : DumperPlugin {
|
||||
|
||||
override fun dump(dumpContext: DumperContext) {
|
||||
val parser = GnuParser()
|
||||
val options = Options()
|
||||
options.addRequiredOption("a", "account", true, "Account key")
|
||||
val cmdLine = try {
|
||||
parser.parse(options, dumpContext.argsAsList.toTypedArray())
|
||||
} catch (e: ParseException) {
|
||||
throw DumpException(e.message)
|
||||
}
|
||||
|
||||
val accountKey = UserKey.valueOf(cmdLine.getOptionValue("account"))
|
||||
val am = AccountManager.get(context)
|
||||
val account = AccountUtils.findByAccountKey(am, accountKey) ?: return
|
||||
val credentials = account.getCredentials(am)
|
||||
val userStream = credentials.newMicroBlogInstance(context, account.type,
|
||||
cls = TwitterUserStream::class.java)
|
||||
dumpContext.stdout.println("Beginning user stream...")
|
||||
dumpContext.stdout.flush()
|
||||
val callback = object : UserStreamCallback() {
|
||||
override fun onException(ex: Throwable): Boolean {
|
||||
ex.printStackTrace(dumpContext.stderr)
|
||||
dumpContext.stderr.flush()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStatus(status: Status): Boolean {
|
||||
dumpContext.stdout.println("Status: @${status.user.screenName}: ${status.text.trim('\n')}")
|
||||
dumpContext.stdout.flush()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDirectMessage(directMessage: DirectMessage): Boolean {
|
||||
dumpContext.stdout.println("Message: @${directMessage.senderScreenName}: ${directMessage.text.trim('\n')}")
|
||||
dumpContext.stdout.flush()
|
||||
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 {
|
||||
userStream.getUserStream(callback)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace(dumpContext.stderr)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName() = "user_stream"
|
||||
|
||||
}
|
|
@ -31,7 +31,4 @@ public interface TwitterUserStream {
|
|||
@GET("/user.json")
|
||||
void getUserStream(UserStreamCallback callback);
|
||||
|
||||
@GET("/user.json")
|
||||
void getUserStreamRaw(RawCallback<MicroBlogException> callback);
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.util.Log;
|
|||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
|
||||
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
|
||||
import org.mariotaku.microblog.library.MicroBlogException;
|
||||
import org.mariotaku.microblog.library.twitter.model.DeletionEvent;
|
||||
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
|
||||
|
@ -46,6 +45,7 @@ import java.io.InputStreamReader;
|
|||
/**
|
||||
* Created by mariotaku on 15/5/26.
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess"})
|
||||
public abstract class UserStreamCallback implements RawCallback<MicroBlogException> {
|
||||
|
||||
private boolean connected;
|
||||
|
@ -53,7 +53,7 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
|
|||
private boolean disconnected;
|
||||
|
||||
@Override
|
||||
public final void result(final HttpResponse response) throws MicroBlogException, IOException {
|
||||
public final void result(@NonNull final HttpResponse response) throws MicroBlogException, IOException {
|
||||
if (!response.isSuccessful()) {
|
||||
final MicroBlogException cause = new MicroBlogException();
|
||||
cause.setHttpResponse(response);
|
||||
|
@ -69,77 +69,8 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
|
|||
}
|
||||
if (TextUtils.isEmpty(line)) continue;
|
||||
final TwitterStreamObject object = LoganSquare.parse(line, TwitterStreamObject.class);
|
||||
switch (object.determine()) {
|
||||
case Type.SENDER: {
|
||||
break;
|
||||
}
|
||||
case Type.STATUS: {
|
||||
onStatus(LoganSquareMapperFinder.mapperFor(Status.class).parse(line));
|
||||
break;
|
||||
}
|
||||
case Type.DIRECT_MESSAGE: {
|
||||
onDirectMessage(object.getDirectMessage());
|
||||
break;
|
||||
}
|
||||
case Type.DELETE: {
|
||||
final TwitterStreamObject.Delete delete = object.getDelete();
|
||||
if (delete.getStatus() != null) {
|
||||
onStatusDeleted(delete.getStatus());
|
||||
} else if (delete.getDirectMessage() != null) {
|
||||
onDirectMessageDeleted(delete.getDirectMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type.LIMIT:
|
||||
break;
|
||||
case Type.STALL_WARNING:
|
||||
break;
|
||||
case Type.SCRUB_GEO:
|
||||
break;
|
||||
case Type.FRIENDS:
|
||||
break;
|
||||
case Type.FAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquareMapperFinder.mapperFor(StatusFavoriteEvent.class).parse(line);
|
||||
onFavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
break;
|
||||
}
|
||||
case Type.UNFAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquareMapperFinder.mapperFor(StatusFavoriteEvent.class).parse(line);
|
||||
onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
break;
|
||||
}
|
||||
case Type.FOLLOW:
|
||||
break;
|
||||
case Type.UNFOLLOW:
|
||||
break;
|
||||
case Type.USER_LIST_MEMBER_ADDED:
|
||||
break;
|
||||
case Type.USER_LIST_MEMBER_DELETED:
|
||||
break;
|
||||
case Type.USER_LIST_SUBSCRIBED:
|
||||
break;
|
||||
case Type.USER_LIST_UNSUBSCRIBED:
|
||||
break;
|
||||
case Type.USER_LIST_CREATED:
|
||||
break;
|
||||
case Type.USER_LIST_UPDATED:
|
||||
break;
|
||||
case Type.USER_LIST_DESTROYED:
|
||||
break;
|
||||
case Type.USER_UPDATE:
|
||||
break;
|
||||
case Type.USER_DELETE:
|
||||
break;
|
||||
case Type.USER_SUSPEND:
|
||||
break;
|
||||
case Type.BLOCK:
|
||||
break;
|
||||
case Type.UNBLOCK:
|
||||
break;
|
||||
case Type.DISCONNECTION:
|
||||
break;
|
||||
case Type.UNKNOWN:
|
||||
break;
|
||||
if (!handleEvent(object, line)) {
|
||||
onUnhandledEvent(object, line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -147,13 +78,89 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
|
|||
} finally {
|
||||
Log.d("Twidere.Stream", "Cleaning up...");
|
||||
reader.close();
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleEvent(final TwitterStreamObject object, final String json) throws IOException {
|
||||
switch (object.determine()) {
|
||||
case Type.SENDER: {
|
||||
break;
|
||||
}
|
||||
case Type.STATUS: {
|
||||
return onStatus(LoganSquare.parse(json, Status.class));
|
||||
}
|
||||
case Type.DIRECT_MESSAGE: {
|
||||
return onDirectMessage(object.getDirectMessage());
|
||||
}
|
||||
case Type.DELETE: {
|
||||
final TwitterStreamObject.Delete delete = object.getDelete();
|
||||
if (delete.getStatus() != null) {
|
||||
return onStatusDeleted(delete.getStatus());
|
||||
} else if (delete.getDirectMessage() != null) {
|
||||
return onDirectMessageDeleted(delete.getDirectMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type.LIMIT: {
|
||||
return onTrackLimitationNotice(object.getLimit().getTrack());
|
||||
}
|
||||
case Type.STALL_WARNING:
|
||||
break;
|
||||
case Type.SCRUB_GEO: {
|
||||
TwitterStreamObject.ScrubGeo scrubGeo = object.getScrubGeo();
|
||||
return onScrubGeo(scrubGeo.getUserId(), scrubGeo.getUpToStatusId());
|
||||
}
|
||||
case Type.FRIENDS: {
|
||||
return onFriendList(object.getFriends());
|
||||
}
|
||||
case Type.FAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquare.parse(json, StatusFavoriteEvent.class);
|
||||
return onFavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
}
|
||||
case Type.UNFAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquare.parse(json, StatusFavoriteEvent.class);
|
||||
return onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
}
|
||||
case Type.FOLLOW:
|
||||
break;
|
||||
case Type.UNFOLLOW:
|
||||
break;
|
||||
case Type.USER_LIST_MEMBER_ADDED:
|
||||
break;
|
||||
case Type.USER_LIST_MEMBER_DELETED:
|
||||
break;
|
||||
case Type.USER_LIST_SUBSCRIBED:
|
||||
break;
|
||||
case Type.USER_LIST_UNSUBSCRIBED:
|
||||
break;
|
||||
case Type.USER_LIST_CREATED:
|
||||
break;
|
||||
case Type.USER_LIST_UPDATED:
|
||||
break;
|
||||
case Type.USER_LIST_DESTROYED:
|
||||
break;
|
||||
case Type.USER_UPDATE:
|
||||
break;
|
||||
case Type.USER_DELETE:
|
||||
break;
|
||||
case Type.USER_SUSPEND:
|
||||
break;
|
||||
case Type.BLOCK:
|
||||
break;
|
||||
case Type.UNBLOCK:
|
||||
break;
|
||||
case Type.DISCONNECTION:
|
||||
TwitterStreamObject.Disconnect disconnect = object.getDisconnect();
|
||||
return onDisconnect(disconnect.getCode(), disconnect.getReason());
|
||||
case Type.UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void error(final MicroBlogException cause) {
|
||||
public final void error(@NonNull final MicroBlogException cause) {
|
||||
onException(cause);
|
||||
}
|
||||
|
||||
|
@ -161,49 +168,102 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
|
|||
disconnected = true;
|
||||
}
|
||||
|
||||
public abstract void onConnected();
|
||||
protected boolean onConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onStatus(Status status) throws IOException;
|
||||
protected boolean onDisconnect(int code, String reason) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onDirectMessage(@NonNull DirectMessage directMessage) throws IOException;
|
||||
protected boolean onStatus(@NonNull Status status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onBlock(User source, User blockedUser);
|
||||
protected boolean onDirectMessage(@NonNull DirectMessage directMessage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onDirectMessageDeleted(DeletionEvent event);
|
||||
protected boolean onBlock(User source, User blockedUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onStatusDeleted(DeletionEvent event);
|
||||
protected boolean onDirectMessageDeleted(@NonNull DeletionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onException(Throwable ex);
|
||||
protected boolean onStatusDeleted(@NonNull DeletionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onFavorite(User source, User target, Status targetStatus);
|
||||
protected boolean onException(@NonNull Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onFollow(User source, User followedUser);
|
||||
protected boolean onFavorite(@NonNull User source, @NonNull User target, @NonNull Status targetStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onFriendList(long[] friendIds);
|
||||
protected boolean onFollow(User source, User followedUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onScrubGeo(long userId, long upToStatusId);
|
||||
protected boolean onFriendList(@NonNull String[] friendIds) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onStallWarning(Warning warn);
|
||||
protected boolean onScrubGeo(String userId, String upToStatusId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onTrackLimitationNotice(int numberOfLimitedStatuses);
|
||||
protected boolean onStallWarning(Warning warn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUnblock(User source, User unblockedUser);
|
||||
protected boolean onTrackLimitationNotice(int numberOfLimitedStatuses) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUnfavorite(User source, User target, Status targetStatus);
|
||||
protected boolean onUnblock(User source, User unblockedUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListCreation(User listOwner, UserList list);
|
||||
protected boolean onUnfavorite(User source, User target, Status targetStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListDeletion(User listOwner, UserList list);
|
||||
protected boolean onUserListCreation(User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListMemberAddition(User addedMember, User listOwner, UserList list);
|
||||
protected boolean onUserListDeletion(User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListMemberDeletion(User deletedMember, User listOwner, UserList list);
|
||||
protected boolean onUserListMemberAddition(User addedMember, User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListSubscription(User subscriber, User listOwner, UserList list);
|
||||
protected boolean onUserListMemberDeletion(User deletedMember, User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListUnsubscription(User subscriber, User listOwner, UserList list);
|
||||
protected boolean onUserListSubscription(User subscriber, User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserListUpdate(User listOwner, UserList list);
|
||||
protected boolean onUserListUnsubscription(User subscriber, User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onUserProfileUpdate(User updatedUser);
|
||||
protected boolean onUserListUpdate(User listOwner, UserList list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean onUserProfileUpdate(User updatedUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void onUnhandledEvent(@NonNull final TwitterStreamObject obj, @NonNull final String json) throws IOException {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,15 +23,15 @@ public class TwitterStreamObject {
|
|||
@JsonField(name = "delete")
|
||||
Delete delete;
|
||||
@JsonField(name = "disconnect")
|
||||
EmptyObject disconnect;
|
||||
Disconnect disconnect;
|
||||
@JsonField(name = "limit")
|
||||
EmptyObject limit;
|
||||
Limit limit;
|
||||
@JsonField(name = "warning")
|
||||
EmptyObject warning;
|
||||
@JsonField(name = "scrub_geo")
|
||||
EmptyObject scrubGeo;
|
||||
ScrubGeo scrubGeo;
|
||||
@JsonField(name = "friends")
|
||||
EmptyObject friends;
|
||||
String[] friends;
|
||||
|
||||
@Type
|
||||
public String determine() {
|
||||
|
@ -103,6 +103,22 @@ public class TwitterStreamObject {
|
|||
return delete;
|
||||
}
|
||||
|
||||
public ScrubGeo getScrubGeo() {
|
||||
return scrubGeo;
|
||||
}
|
||||
|
||||
public Limit getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public Disconnect getDisconnect() {
|
||||
return disconnect;
|
||||
}
|
||||
|
||||
public String[] getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
@StringDef({Type.SENDER, Type.STATUS, Type.DIRECT_MESSAGE, Type.DELETE, Type.LIMIT,
|
||||
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,
|
||||
|
@ -158,4 +174,53 @@ public class TwitterStreamObject {
|
|||
return directMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonObject
|
||||
public static class ScrubGeo {
|
||||
@JsonField(name = "user_id")
|
||||
String userId;
|
||||
|
||||
@JsonField(name = "up_to_status_id")
|
||||
String upToStatusId;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getUpToStatusId() {
|
||||
return upToStatusId;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonObject
|
||||
public static class Limit {
|
||||
@JsonField(name = "track")
|
||||
int track;
|
||||
|
||||
public int getTrack() {
|
||||
return track;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonObject
|
||||
public static class Disconnect {
|
||||
@JsonField(name = "code")
|
||||
int code;
|
||||
@JsonField(name = "stream_name")
|
||||
String streamName;
|
||||
@JsonField(name = "reason")
|
||||
String reason;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getStreamName() {
|
||||
return streamName;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ fun String?.toDouble(def: Double): Double {
|
|||
}
|
||||
}
|
||||
|
||||
fun Int.coerceInOr(range: ClosedRange<Int>, or: Int): Int {
|
||||
if (range.isEmpty()) return or
|
||||
fun Int.coerceInOr(range: ClosedRange<Int>, def: Int): Int {
|
||||
if (range.isEmpty()) return def
|
||||
return coerceIn(range)
|
||||
}
|
||||
|
||||
|
|
|
@ -149,21 +149,21 @@ class WebLinkHandlerActivity : Activity() {
|
|||
val builder = Uri.Builder()
|
||||
builder.scheme(SCHEME_TWIDERE)
|
||||
builder.authority(AUTHORITY_USER_FRIENDS)
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF_REFERENCE.toString())
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF.toString())
|
||||
return Pair(Intent(Intent.ACTION_VIEW, builder.build()), true)
|
||||
}
|
||||
"followers" -> {
|
||||
val builder = Uri.Builder()
|
||||
builder.scheme(SCHEME_TWIDERE)
|
||||
builder.authority(AUTHORITY_USER_FOLLOWERS)
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF_REFERENCE.toString())
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF.toString())
|
||||
return Pair(Intent(Intent.ACTION_VIEW, builder.build()), true)
|
||||
}
|
||||
"favorites" -> {
|
||||
val builder = Uri.Builder()
|
||||
builder.scheme(SCHEME_TWIDERE)
|
||||
builder.authority(AUTHORITY_USER_FAVORITES)
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF_REFERENCE.toString())
|
||||
builder.appendQueryParameter(QUERY_PARAM_USER_KEY, UserKey.SELF.toString())
|
||||
return Pair(Intent(Intent.ACTION_VIEW, builder.build()), true)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.support.v4.text.BidiFormatter
|
|||
import android.support.v7.widget.RecyclerView
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IUserListsAdapter
|
||||
|
@ -13,7 +12,10 @@ import org.mariotaku.twidere.adapter.iface.IUsersAdapter
|
|||
import org.mariotaku.twidere.constant.*
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.util.getActivityStatus
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper
|
||||
import org.mariotaku.twidere.util.TwidereLinkify
|
||||
import org.mariotaku.twidere.util.UserColorNameManager
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
|
||||
import javax.inject.Inject
|
||||
|
@ -72,42 +74,28 @@ class DummyItemAdapter(
|
|||
return 0
|
||||
}
|
||||
|
||||
override fun getStatus(position: Int): ParcelableStatus? {
|
||||
override fun getStatus(position: Int, raw: Boolean): ParcelableStatus {
|
||||
if (adapter is ParcelableStatusesAdapter) {
|
||||
return adapter.getStatus(position)
|
||||
return adapter.getStatus(position, raw)
|
||||
} else if (adapter is VariousItemsAdapter) {
|
||||
return adapter.getItem(position) as ParcelableStatus
|
||||
} else if (adapter is ParcelableActivitiesAdapter) {
|
||||
return adapter.getActivity(position)?.getActivityStatus()
|
||||
return adapter.getActivity(position)?.getActivityStatus()!!
|
||||
}
|
||||
return null
|
||||
throw IndexOutOfBoundsException()
|
||||
}
|
||||
|
||||
override val statusCount: Int
|
||||
get() = 0
|
||||
override fun getStatusCount(raw: Boolean) = 0
|
||||
|
||||
override val rawStatusCount: Int
|
||||
get() = 0
|
||||
override fun getStatusId(position: Int, raw: Boolean) = ""
|
||||
|
||||
override fun getStatusId(position: Int): String? {
|
||||
return null
|
||||
}
|
||||
override fun getStatusTimestamp(position: Int, raw: Boolean) = -1L
|
||||
|
||||
override fun getStatusTimestamp(position: Int): Long {
|
||||
return -1
|
||||
}
|
||||
override fun getStatusPositionKey(position: Int, raw: Boolean) = -1L
|
||||
|
||||
override fun getStatusPositionKey(position: Int): Long {
|
||||
return -1
|
||||
}
|
||||
override fun getAccountKey(position: Int, raw: Boolean) = UserKey.INVALID
|
||||
|
||||
override fun getAccountKey(position: Int): UserKey? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun findStatusById(accountKey: UserKey, statusId: String): ParcelableStatus? {
|
||||
return null
|
||||
}
|
||||
override fun findStatusById(accountKey: UserKey, statusId: String) = null
|
||||
|
||||
override fun isCardActionsShown(position: Int): Boolean {
|
||||
if (position == RecyclerView.NO_POSITION) return showCardActions
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.support.v4.widget.Space
|
||||
import android.support.v7.widget.RecyclerView
|
||||
|
@ -36,6 +37,7 @@ import org.mariotaku.microblog.library.twitter.model.Activity
|
|||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.iface.IActivitiesAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.annotation.Referral
|
||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API
|
||||
|
@ -58,7 +60,10 @@ import java.util.*
|
|||
class ParcelableActivitiesAdapter(
|
||||
context: Context,
|
||||
requestManager: RequestManager
|
||||
) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context, requestManager), IActivitiesAdapter<List<ParcelableActivity>> {
|
||||
) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context, requestManager),
|
||||
IActivitiesAdapter<List<ParcelableActivity>>, IItemCountsAdapter {
|
||||
|
||||
override val itemCounts: ItemCounts = ItemCounts(2)
|
||||
|
||||
private val inflater = LayoutInflater.from(context)
|
||||
private val twidereLinkify = TwidereLinkify(OnLinkClickHandler(context, null, preferences))
|
||||
|
@ -80,6 +85,20 @@ class ParcelableActivitiesAdapter(
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override var loadMoreIndicatorPosition: Long
|
||||
get() = super.loadMoreIndicatorPosition
|
||||
set(value) {
|
||||
super.loadMoreIndicatorPosition = value
|
||||
updateItemCount()
|
||||
}
|
||||
|
||||
override var loadMoreSupportedPosition: Long
|
||||
get() = super.loadMoreSupportedPosition
|
||||
set(value) {
|
||||
super.loadMoreSupportedPosition = value
|
||||
updateItemCount()
|
||||
}
|
||||
|
||||
init {
|
||||
eventListener = EventListener(this)
|
||||
statusAdapterDelegate.updateOptions()
|
||||
|
@ -87,7 +106,7 @@ class ParcelableActivitiesAdapter(
|
|||
|
||||
override fun isGapItem(position: Int): Boolean {
|
||||
val dataPosition = position - activityStartIndex
|
||||
val activityCount = activityCount
|
||||
val activityCount = getActivityCount(false)
|
||||
if (dataPosition < 0 || dataPosition >= activityCount) return false
|
||||
// Don't show gap if it's last item
|
||||
if (dataPosition == activityCount - 1) {
|
||||
|
@ -104,7 +123,6 @@ class ParcelableActivitiesAdapter(
|
|||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val dataPosition = position - activityStartIndex
|
||||
if (dataPosition < 0 || dataPosition >= activityCount) return RecyclerView.NO_ID
|
||||
if (data is ObjectCursor) {
|
||||
val cursor = (data as ObjectCursor).cursor
|
||||
if (!cursor.moveToPosition(dataPosition)) return -1
|
||||
|
@ -116,47 +134,37 @@ class ParcelableActivitiesAdapter(
|
|||
return ParcelableActivity.calculateHashCode(accountKey, timestamp, maxPosition,
|
||||
minPosition).toLong()
|
||||
}
|
||||
return data!![dataPosition].hashCode().toLong()
|
||||
return getActivity(position, false).hashCode().toLong()
|
||||
}
|
||||
|
||||
fun getActivityAction(adapterPosition: Int): String? {
|
||||
fun getTimestamp(adapterPosition: Int, raw: Boolean = false): Long {
|
||||
val dataPosition = adapterPosition - activityStartIndex
|
||||
if (dataPosition < 0 || dataPosition >= activityCount) return null
|
||||
if (data is ObjectCursor) {
|
||||
val cursor = (data as ObjectCursor).cursor
|
||||
if (!cursor.safeMoveToPosition(dataPosition)) return null
|
||||
val indices = (data as ObjectCursor).indices as ParcelableActivityCursorIndices
|
||||
return cursor.getString(indices.action)
|
||||
}
|
||||
return data!![dataPosition].action
|
||||
}
|
||||
|
||||
fun getTimestamp(adapterPosition: Int): Long {
|
||||
val dataPosition = adapterPosition - activityStartIndex
|
||||
if (dataPosition < 0 || dataPosition >= activityCount) return RecyclerView.NO_ID
|
||||
if (dataPosition < 0 || dataPosition >= getActivityCount(raw)) return RecyclerView.NO_ID
|
||||
if (data is ObjectCursor) {
|
||||
val cursor = (data as ObjectCursor).cursor
|
||||
if (!cursor.safeMoveToPosition(dataPosition)) return -1
|
||||
val indices = (data as ObjectCursor).indices as ParcelableActivityCursorIndices
|
||||
return cursor.getLong(indices.timestamp)
|
||||
}
|
||||
return data!![dataPosition].timestamp
|
||||
return getActivity(adapterPosition, raw).timestamp
|
||||
}
|
||||
|
||||
override fun getActivity(position: Int): ParcelableActivity? {
|
||||
override fun getActivity(position: Int, raw: Boolean): ParcelableActivity {
|
||||
val dataPosition = position - activityStartIndex
|
||||
if (dataPosition < 0 || dataPosition >= activityCount) return null
|
||||
if (dataPosition < 0 || dataPosition >= data!!.size) {
|
||||
val validRange = rangeOfSize(activityStartIndex, getActivityCount(raw))
|
||||
throw IndexOutOfBoundsException("index: $position, valid range is $validRange")
|
||||
}
|
||||
return data!![dataPosition]
|
||||
}
|
||||
|
||||
override val activityCount: Int
|
||||
get() {
|
||||
if (data == null) return 0
|
||||
return data!!.size
|
||||
}
|
||||
override fun getActivityCount(raw: Boolean): Int {
|
||||
if (data == null) return 0
|
||||
return data!!.size
|
||||
}
|
||||
|
||||
private fun bindTitleSummaryViewHolder(holder: ActivityTitleSummaryViewHolder, position: Int) {
|
||||
holder.displayActivity(getActivity(position)!!)
|
||||
holder.displayActivity(getActivity(position))
|
||||
}
|
||||
|
||||
fun getData(): List<ParcelableActivity>? {
|
||||
|
@ -169,6 +177,7 @@ class ParcelableActivitiesAdapter(
|
|||
}
|
||||
this.data = data
|
||||
gapLoadingIds.clear()
|
||||
updateItemCount()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
@ -225,7 +234,7 @@ class ParcelableActivitiesAdapter(
|
|||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder.itemViewType) {
|
||||
ITEM_VIEW_TYPE_STATUS -> {
|
||||
val status = getActivity(position)?.getActivityStatus() ?: return
|
||||
val status = getActivity(position).getActivityStatus() ?: return
|
||||
val statusViewHolder = holder as IStatusViewHolder
|
||||
statusViewHolder.displayStatus(status = status, displayInReplyTo = true)
|
||||
}
|
||||
|
@ -233,10 +242,10 @@ class ParcelableActivitiesAdapter(
|
|||
bindTitleSummaryViewHolder(holder as ActivityTitleSummaryViewHolder, position)
|
||||
}
|
||||
ITEM_VIEW_TYPE_STUB -> {
|
||||
(holder as StubViewHolder).displayActivity(getActivity(position)!!)
|
||||
(holder as StubViewHolder).displayActivity(getActivity(position))
|
||||
}
|
||||
ITEM_VIEW_TYPE_GAP -> {
|
||||
val activity = getActivity(position)!!
|
||||
val activity = getActivity(position)
|
||||
val loading = gapLoadingIds.any { it.accountKey == activity.account_key && it.id == activity.id }
|
||||
(holder as GapViewHolder).display(loading)
|
||||
}
|
||||
|
@ -244,51 +253,54 @@ class ParcelableActivitiesAdapter(
|
|||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == 0 && ILoadMoreSupportAdapter.START in loadMoreIndicatorPosition) {
|
||||
return ITEM_VIEW_TYPE_LOAD_INDICATOR
|
||||
} else if (position == activityCount) {
|
||||
return ITEM_VIEW_TYPE_LOAD_INDICATOR
|
||||
} else if (isGapItem(position)) {
|
||||
return ITEM_VIEW_TYPE_GAP
|
||||
}
|
||||
val action = getActivityAction(position) ?: return ITEM_VIEW_TYPE_EMPTY
|
||||
val activity = getActivity(position) ?: return ITEM_VIEW_TYPE_EMPTY
|
||||
when (action) {
|
||||
Activity.Action.MENTION -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_object_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
when (getItemCountIndex(position)) {
|
||||
ITEM_INDEX_ACTIVITY -> {
|
||||
if (isGapItem(position)) {
|
||||
return ITEM_VIEW_TYPE_GAP
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.REPLY -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.QUOTE -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.FOLLOW, Activity.Action.FAVORITE, Activity.Action.RETWEET,
|
||||
Activity.Action.FAVORITED_RETWEET, Activity.Action.RETWEETED_RETWEET,
|
||||
Activity.Action.RETWEETED_MENTION, Activity.Action.FAVORITED_MENTION,
|
||||
Activity.Action.LIST_CREATED, Activity.Action.LIST_MEMBER_ADDED,
|
||||
Activity.Action.MEDIA_TAGGED, Activity.Action.RETWEETED_MEDIA_TAGGED,
|
||||
Activity.Action.FAVORITED_MEDIA_TAGGED, Activity.Action.JOINED_TWITTER -> {
|
||||
if (mentionsOnly) return ITEM_VIEW_TYPE_EMPTY
|
||||
filteredUserIds?.let {
|
||||
ParcelableActivityUtils.initAfterFilteredSourceIds(activity, it, followingOnly)
|
||||
if (activity.after_filtered_source_ids.isEmpty()) {
|
||||
return ITEM_VIEW_TYPE_EMPTY
|
||||
val activity = getActivity(position)
|
||||
when (activity.action) {
|
||||
Activity.Action.MENTION -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_object_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.REPLY -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.QUOTE -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_statuses)) {
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STATUS
|
||||
}
|
||||
Activity.Action.FOLLOW, Activity.Action.FAVORITE, Activity.Action.RETWEET,
|
||||
Activity.Action.FAVORITED_RETWEET, Activity.Action.RETWEETED_RETWEET,
|
||||
Activity.Action.RETWEETED_MENTION, Activity.Action.FAVORITED_MENTION,
|
||||
Activity.Action.LIST_CREATED, Activity.Action.LIST_MEMBER_ADDED,
|
||||
Activity.Action.MEDIA_TAGGED, Activity.Action.RETWEETED_MEDIA_TAGGED,
|
||||
Activity.Action.FAVORITED_MEDIA_TAGGED, Activity.Action.JOINED_TWITTER -> {
|
||||
if (mentionsOnly) return ITEM_VIEW_TYPE_EMPTY
|
||||
filteredUserIds?.let {
|
||||
ParcelableActivityUtils.initAfterFilteredSourceIds(activity, it, followingOnly)
|
||||
if (activity.after_filtered_source_ids.isEmpty()) {
|
||||
return ITEM_VIEW_TYPE_EMPTY
|
||||
}
|
||||
}
|
||||
return ITEM_VIEW_TYPE_TITLE_SUMMARY
|
||||
}
|
||||
}
|
||||
return ITEM_VIEW_TYPE_TITLE_SUMMARY
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
}
|
||||
ITEM_INDEX_LOAD_MORE_INDICATOR -> {
|
||||
return ITEM_VIEW_TYPE_LOAD_INDICATOR
|
||||
}
|
||||
}
|
||||
return ITEM_VIEW_TYPE_STUB
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun addGapLoadingId(id: ObjectId) {
|
||||
|
@ -300,16 +312,7 @@ class ParcelableActivitiesAdapter(
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
val position = loadMoreIndicatorPosition
|
||||
var count = 0
|
||||
if (position and ILoadMoreSupportAdapter.START != 0L) {
|
||||
count += 1
|
||||
}
|
||||
count += activityCount
|
||||
if (position and ILoadMoreSupportAdapter.END != 0L) {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
return itemCounts.itemCount
|
||||
}
|
||||
|
||||
fun setListener(listener: ActivityAdapterListener) {
|
||||
|
@ -330,23 +333,16 @@ class ParcelableActivitiesAdapter(
|
|||
}
|
||||
|
||||
|
||||
fun isActivity(position: Int): Boolean {
|
||||
return position < activityCount
|
||||
fun isActivity(position: Int, raw: Boolean = false): Boolean {
|
||||
return position < getActivityCount(raw)
|
||||
}
|
||||
|
||||
val activityStartIndex: Int
|
||||
get() {
|
||||
val position = loadMoreIndicatorPosition
|
||||
var start = 0
|
||||
if (position and ILoadMoreSupportAdapter.START != 0L) {
|
||||
start += 1
|
||||
}
|
||||
return start
|
||||
}
|
||||
get() = getItemStartPosition(ITEM_INDEX_ACTIVITY)
|
||||
|
||||
fun findPositionBySortTimestamp(timestamp: Long): Int {
|
||||
fun findPositionBySortTimestamp(timestamp: Long, raw: Boolean = false): Int {
|
||||
if (timestamp <= 0) return RecyclerView.NO_POSITION
|
||||
val range = rangeOfSize(activityStartIndex, activityCount)
|
||||
val range = rangeOfSize(activityStartIndex, getActivityCount(raw))
|
||||
if (range.isEmpty()) return RecyclerView.NO_POSITION
|
||||
if (timestamp < getTimestamp(range.last)) {
|
||||
return range.last
|
||||
|
@ -354,6 +350,11 @@ class ParcelableActivitiesAdapter(
|
|||
return range.indexOfFirst { timestamp >= getTimestamp(it) }
|
||||
}
|
||||
|
||||
private fun updateItemCount() {
|
||||
itemCounts[0] = getActivityCount(false)
|
||||
itemCounts[1] = if (ILoadMoreSupportAdapter.END in loadMoreIndicatorPosition) 1 else 0
|
||||
}
|
||||
|
||||
interface ActivityAdapterListener {
|
||||
fun onGapClick(holder: GapViewHolder, position: Int)
|
||||
|
||||
|
@ -383,6 +384,7 @@ class ParcelableActivitiesAdapter(
|
|||
text2.setSingleLine(false)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun displayActivity(activity: ParcelableActivity) {
|
||||
text1.text = text1.resources.getString(R.string.unsupported_activity_action_title,
|
||||
activity.action)
|
||||
|
@ -397,7 +399,7 @@ class ParcelableActivitiesAdapter(
|
|||
|
||||
override fun onGapClick(holder: GapViewHolder, position: Int) {
|
||||
val adapter = adapterRef.get() ?: return
|
||||
val activity = adapter.getActivity(position) ?: return
|
||||
val activity = adapter.getActivity(position)
|
||||
adapter.addGapLoadingId(ObjectId(activity.account_key, activity.id))
|
||||
adapter.activityAdapterListener?.onGapClick(holder, position)
|
||||
}
|
||||
|
@ -418,7 +420,7 @@ class ParcelableActivitiesAdapter(
|
|||
|
||||
override fun onUserProfileClick(holder: IStatusViewHolder, position: Int) {
|
||||
val adapter = adapterRef.get() ?: return
|
||||
val status = adapter.getActivity(position)?.getActivityStatus() ?: return
|
||||
val status = adapter.getActivity(position).getActivityStatus() ?: return
|
||||
IntentUtils.openUserProfile(adapter.context, status.account_key, status.user_key,
|
||||
status.user_screen_name, adapter.preferences.getBoolean(KEY_NEW_DOCUMENT_API), Referral.TIMELINE_STATUS,
|
||||
null)
|
||||
|
@ -459,5 +461,8 @@ class ParcelableActivitiesAdapter(
|
|||
const val ITEM_VIEW_TYPE_STATUS = 4
|
||||
const val ITEM_VIEW_TYPE_EMPTY = 5
|
||||
|
||||
const val ITEM_INDEX_ACTIVITY = 0
|
||||
const val ITEM_INDEX_LOAD_MORE_INDICATOR = 1
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ abstract class ParcelableStatusesAdapter(
|
|||
|
||||
override fun isGapItem(position: Int): Boolean {
|
||||
val dataPosition = position - statusStartIndex
|
||||
val statusCount = statusCount
|
||||
val statusCount = getStatusCount(false)
|
||||
if (dataPosition < 0 || dataPosition >= statusCount) return false
|
||||
// Don't show gap if it's last item
|
||||
if (dataPosition == statusCount - 1) return false
|
||||
|
@ -153,18 +153,17 @@ abstract class ParcelableStatusesAdapter(
|
|||
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
|
||||
return cursor.getShort(indices.is_gap).toInt() == 1
|
||||
}
|
||||
return getStatus(position)!!.is_gap
|
||||
return getStatus(position).is_gap
|
||||
}
|
||||
|
||||
override fun getStatus(position: Int): ParcelableStatus? {
|
||||
return getStatus(position, getItemCountIndex(position))
|
||||
override fun getStatus(position: Int, raw: Boolean): ParcelableStatus {
|
||||
return getStatus(position, getItemCountIndex(position, raw), raw)
|
||||
}
|
||||
|
||||
override val statusCount: Int
|
||||
get() = displayDataCount
|
||||
|
||||
override val rawStatusCount: Int
|
||||
get() = data?.size ?: 0
|
||||
override fun getStatusCount(raw: Boolean): Int {
|
||||
if (raw) return data?.size ?: 0
|
||||
return displayDataCount
|
||||
}
|
||||
|
||||
override fun setData(data: List<ParcelableStatus>?): Boolean {
|
||||
var changed = true
|
||||
|
@ -186,7 +185,7 @@ abstract class ParcelableStatusesAdapter(
|
|||
}
|
||||
}
|
||||
displayDataCount = data.size - filteredCount
|
||||
changed = data != data
|
||||
changed = this.data != data
|
||||
}
|
||||
this.data = data
|
||||
gapLoadingIds.clear()
|
||||
|
@ -218,13 +217,12 @@ abstract class ParcelableStatusesAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getStatusId(position: Int): String? {
|
||||
val def: String? = null
|
||||
override fun getStatusId(position: Int, raw: Boolean): String {
|
||||
return getFieldValue(position, { cursor, indices ->
|
||||
return@getFieldValue cursor.getString(indices.id)
|
||||
}, { status ->
|
||||
return@getFieldValue status.id
|
||||
}, def)
|
||||
}, "")
|
||||
}
|
||||
|
||||
fun getStatusSortId(position: Int): Long {
|
||||
|
@ -235,7 +233,7 @@ abstract class ParcelableStatusesAdapter(
|
|||
}, -1L)
|
||||
}
|
||||
|
||||
override fun getStatusTimestamp(position: Int): Long {
|
||||
override fun getStatusTimestamp(position: Int, raw: Boolean): Long {
|
||||
return getFieldValue(position, { cursor, indices ->
|
||||
return@getFieldValue cursor.getLong(indices.timestamp)
|
||||
}, { status ->
|
||||
|
@ -243,7 +241,7 @@ abstract class ParcelableStatusesAdapter(
|
|||
}, -1L)
|
||||
}
|
||||
|
||||
override fun getStatusPositionKey(position: Int): Long {
|
||||
override fun getStatusPositionKey(position: Int, raw: Boolean): Long {
|
||||
return getFieldValue(position, { cursor, indices ->
|
||||
val positionKey = cursor.getLong(indices.position_key)
|
||||
if (positionKey > 0) return@getFieldValue positionKey
|
||||
|
@ -255,13 +253,13 @@ abstract class ParcelableStatusesAdapter(
|
|||
}, -1L)
|
||||
}
|
||||
|
||||
override fun getAccountKey(position: Int): UserKey? {
|
||||
override fun getAccountKey(position: Int, raw: Boolean): UserKey {
|
||||
val def: UserKey? = null
|
||||
return getFieldValue(position, { cursor, indices ->
|
||||
return@getFieldValue UserKey.valueOf(cursor.getString(indices.account_key))
|
||||
}, { status ->
|
||||
return@getFieldValue status.account_key
|
||||
}, def)
|
||||
}, def, raw)!!
|
||||
}
|
||||
|
||||
override fun isCardActionsShown(position: Int): Boolean {
|
||||
|
@ -307,12 +305,12 @@ abstract class ParcelableStatusesAdapter(
|
|||
when (holder.itemViewType) {
|
||||
VIEW_TYPE_STATUS -> {
|
||||
val countIdx = getItemCountIndex(position)
|
||||
val status = getStatus(position, countIdx)!!
|
||||
val status = getStatus(position, countIdx)
|
||||
(holder as IStatusViewHolder).displayStatus(status, displayInReplyTo = isShowInReplyTo,
|
||||
displayPinned = countIdx == ITEM_INDEX_PINNED_STATUS)
|
||||
}
|
||||
ITEM_VIEW_TYPE_GAP -> {
|
||||
val status = getStatus(position)!!
|
||||
val status = getStatus(position)
|
||||
val loading = gapLoadingIds.any { it.accountKey == status.account_key && it.id == status.id }
|
||||
(holder as GapViewHolder).display(loading)
|
||||
}
|
||||
|
@ -351,8 +349,8 @@ abstract class ParcelableStatusesAdapter(
|
|||
gapLoadingIds.remove(id)
|
||||
}
|
||||
|
||||
fun isStatus(position: Int): Boolean {
|
||||
return position < statusCount
|
||||
fun isStatus(position: Int, raw: Boolean = false): Boolean {
|
||||
return position < getStatusCount(raw)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
|
@ -360,19 +358,19 @@ abstract class ParcelableStatusesAdapter(
|
|||
}
|
||||
|
||||
override fun findStatusById(accountKey: UserKey, statusId: String): ParcelableStatus? {
|
||||
for (i in 0 until statusCount) {
|
||||
if (accountKey == getAccountKey(i) && statusId == getStatusId(i)) {
|
||||
return getStatus(i)
|
||||
for (i in 0 until getStatusCount(true)) {
|
||||
if (accountKey == getAccountKey(i, true) && statusId == getStatusId(i, true)) {
|
||||
return getStatus(i, true)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun findPositionByPositionKey(positionKey: Long): Int {
|
||||
fun findPositionByPositionKey(positionKey: Long, raw: Boolean = false): Int {
|
||||
// Assume statuses are descend sorted by id, so break at first status with id
|
||||
// lesser equals than read position
|
||||
if (positionKey <= 0) return RecyclerView.NO_POSITION
|
||||
val range = rangeOfSize(statusStartIndex, statusCount)
|
||||
val range = rangeOfSize(statusStartIndex, getStatusCount(raw))
|
||||
if (range.isEmpty()) return RecyclerView.NO_POSITION
|
||||
if (positionKey < getStatusPositionKey(range.last)) {
|
||||
return range.last
|
||||
|
@ -380,11 +378,11 @@ abstract class ParcelableStatusesAdapter(
|
|||
return range.indexOfFirst { positionKey >= getStatusPositionKey(it) }
|
||||
}
|
||||
|
||||
fun findPositionBySortId(sortId: Long): Int {
|
||||
fun findPositionBySortId(sortId: Long, raw: Boolean = false): Int {
|
||||
// Assume statuses are descend sorted by id, so break at first status with id
|
||||
// lesser equals than read position
|
||||
if (sortId <= 0) return RecyclerView.NO_POSITION
|
||||
val range = rangeOfSize(statusStartIndex, statusCount)
|
||||
val range = rangeOfSize(statusStartIndex, getStatusCount(raw))
|
||||
if (range.isEmpty()) return RecyclerView.NO_POSITION
|
||||
if (sortId < getStatusSortId(range.last)) {
|
||||
return range.last
|
||||
|
@ -392,48 +390,62 @@ abstract class ParcelableStatusesAdapter(
|
|||
return range.indexOfFirst { sortId >= getStatusSortId(it) }
|
||||
}
|
||||
|
||||
private inline fun <T> getFieldValue(
|
||||
position: Int,
|
||||
private fun getItemCountIndex(position: Int, raw: Boolean): Int {
|
||||
if (!raw) return itemCounts.getItemCountIndex(position)
|
||||
var sum = 0
|
||||
for (i in 0 until itemCounts.size) {
|
||||
sum += when (i) {
|
||||
ITEM_INDEX_STATUS -> data!!.size
|
||||
else -> itemCounts[i]
|
||||
}
|
||||
if (position < sum) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
private inline fun <T> getFieldValue(position: Int,
|
||||
readCursorValueAction: (cursor: Cursor, indices: ParcelableStatusCursorIndices) -> T,
|
||||
readStatusValueAction: (status: ParcelableStatus) -> T,
|
||||
defValue: T
|
||||
): T {
|
||||
defValue: T, raw: Boolean = false): T {
|
||||
if (data is ObjectCursor) {
|
||||
val dataPosition = position - getItemStartPosition(ITEM_INDEX_STATUS)
|
||||
if (dataPosition < 0 || dataPosition >= rawStatusCount) return defValue
|
||||
val dataPosition = position - statusStartIndex
|
||||
if (dataPosition < 0 || dataPosition >= getStatusCount(true)) return defValue
|
||||
val cursor = (data as ObjectCursor).cursor
|
||||
if (!cursor.safeMoveToPosition(dataPosition)) return defValue
|
||||
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
|
||||
return readCursorValueAction(cursor, indices)
|
||||
}
|
||||
return readStatusValueAction(getStatus(position)!!)
|
||||
return readStatusValueAction(getStatus(position))
|
||||
}
|
||||
|
||||
private fun getStatus(position: Int, countIndex: Int): ParcelableStatus? {
|
||||
private fun getStatus(position: Int, countIndex: Int, raw: Boolean = false): ParcelableStatus {
|
||||
when (countIndex) {
|
||||
ITEM_INDEX_PINNED_STATUS -> {
|
||||
return pinnedStatuses!![position - getItemStartPosition(ITEM_INDEX_PINNED_STATUS)]
|
||||
}
|
||||
ITEM_INDEX_STATUS -> {
|
||||
val data = this.data!!
|
||||
val dataPosition = position - getItemStartPosition(ITEM_INDEX_STATUS)
|
||||
val dataPosition = position - statusStartIndex
|
||||
val positions = displayPositions
|
||||
if (positions != null) {
|
||||
if (positions != null && !raw) {
|
||||
return data[positions[dataPosition]]
|
||||
} else {
|
||||
return data[dataPosition]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
val validStart = getItemStartPosition(ITEM_INDEX_PINNED_STATUS)
|
||||
val validEnd = getItemStartPosition(ITEM_INDEX_STATUS) + getStatusCount(raw) - 1
|
||||
throw IndexOutOfBoundsException("index: $position, valid range is $validStart..$validEnd")
|
||||
}
|
||||
|
||||
private fun updateItemCount() {
|
||||
val position = loadMoreIndicatorPosition
|
||||
itemCounts[ITEM_INDEX_LOAD_START_INDICATOR] = if (position and ILoadMoreSupportAdapter.START != 0L) 1 else 0
|
||||
itemCounts[ITEM_INDEX_LOAD_START_INDICATOR] = if (ILoadMoreSupportAdapter.START in loadMoreIndicatorPosition) 1 else 0
|
||||
itemCounts[ITEM_INDEX_PINNED_STATUS] = pinnedStatuses?.size ?: 0
|
||||
itemCounts[ITEM_INDEX_STATUS] = statusCount
|
||||
itemCounts[ITEM_INDEX_LOAD_END_INDICATOR] = if (position and ILoadMoreSupportAdapter.END != 0L) 1 else 0
|
||||
itemCounts[ITEM_INDEX_STATUS] = getStatusCount(false)
|
||||
itemCounts[ITEM_INDEX_LOAD_END_INDICATOR] = if (ILoadMoreSupportAdapter.END in loadMoreIndicatorPosition) 1 else 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -27,8 +27,6 @@ import org.mariotaku.twidere.view.holder.ActivityTitleSummaryViewHolder
|
|||
*/
|
||||
interface IActivitiesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
||||
|
||||
val activityCount: Int
|
||||
|
||||
val mediaPreviewStyle: Int
|
||||
|
||||
val mediaPreviewEnabled: Boolean
|
||||
|
@ -41,7 +39,9 @@ interface IActivitiesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
|||
|
||||
val lightFont: Boolean
|
||||
|
||||
fun getActivity(position: Int): ParcelableActivity?
|
||||
fun getActivityCount(raw: Boolean = false): Int
|
||||
|
||||
fun getActivity(position: Int, raw: Boolean = false): ParcelableActivity
|
||||
|
||||
fun setData(data: Data?)
|
||||
|
||||
|
|
|
@ -19,10 +19,6 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
|||
@PreviewStyle
|
||||
val mediaPreviewStyle: Int
|
||||
|
||||
val statusCount: Int
|
||||
|
||||
val rawStatusCount: Int
|
||||
|
||||
val twidereLinkify: TwidereLinkify
|
||||
|
||||
val mediaPreviewEnabled: Boolean
|
||||
|
@ -43,15 +39,20 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
|||
|
||||
fun setData(data: Data?): Boolean
|
||||
|
||||
fun getStatus(position: Int): ParcelableStatus?
|
||||
/**
|
||||
* @param raw Count hidden (filtered) item if `true `
|
||||
*/
|
||||
fun getStatusCount(raw: Boolean = false): Int
|
||||
|
||||
fun getStatusId(position: Int): String?
|
||||
fun getStatus(position: Int, raw: Boolean = false): ParcelableStatus
|
||||
|
||||
fun getStatusTimestamp(position: Int): Long
|
||||
fun getStatusId(position: Int, raw: Boolean = false): String
|
||||
|
||||
fun getStatusPositionKey(position: Int): Long
|
||||
fun getStatusTimestamp(position: Int, raw: Boolean = false): Long
|
||||
|
||||
fun getAccountKey(position: Int): UserKey?
|
||||
fun getStatusPositionKey(position: Int, raw: Boolean = false): Long
|
||||
|
||||
fun getAccountKey(position: Int, raw: Boolean = false): UserKey
|
||||
|
||||
fun findStatusById(accountKey: UserKey, statusId: String): ParcelableStatus?
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.mariotaku.twidere.fragment
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
|
@ -40,6 +41,7 @@ import org.mariotaku.kpreferences.get
|
|||
import org.mariotaku.ktextension.coerceInOr
|
||||
import org.mariotaku.ktextension.isNullOrEmpty
|
||||
import org.mariotaku.ktextension.rangeOfSize
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_GAP
|
||||
|
@ -64,6 +66,7 @@ import org.mariotaku.twidere.model.event.StatusListChangedEvent
|
|||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
|
||||
import org.mariotaku.twidere.model.util.getActivityStatus
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
|
||||
import org.mariotaku.twidere.util.glide.PauseRecyclerViewOnScrollListener
|
||||
|
@ -134,7 +137,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
position = recyclerView.getChildLayoutPosition(focusedChild)
|
||||
}
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
val activity = adapter.getActivity(position) ?: return false
|
||||
val activity = adapter.getActivity(position)
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
openActivity(activity)
|
||||
return true
|
||||
|
@ -239,7 +242,8 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||
wasAtTop = firstVisibleItemPosition == 0
|
||||
val activityRange = rangeOfSize(adapter.activityStartIndex, Math.max(0, adapter.activityCount))
|
||||
// Get display range of activities
|
||||
val activityRange = rangeOfSize(adapter.activityStartIndex, adapter.getActivityCount(raw = false))
|
||||
val lastReadPosition = if (loadMore || readFromBottom) {
|
||||
lastVisibleItemPosition
|
||||
} else {
|
||||
|
@ -247,7 +251,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
}.coerceInOr(activityRange, -1)
|
||||
lastReadId = adapter.getTimestamp(lastReadPosition)
|
||||
lastReadViewTop = layoutManager.findViewByPosition(lastReadPosition)?.top ?: 0
|
||||
loadMore = activityRange.endInclusive >= 0 && lastVisibleItemPosition >= activityRange.endInclusive
|
||||
loadMore = activityRange.endInclusive in 0..lastVisibleItemPosition
|
||||
} else if (rememberPosition && readPositionTag != null) {
|
||||
lastReadId = readStateManager.getPosition(readPositionTag)
|
||||
lastReadViewTop = 0
|
||||
|
@ -304,17 +308,19 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
}
|
||||
|
||||
override fun onGapClick(holder: GapViewHolder, position: Int) {
|
||||
val activity = adapter.getActivity(position) ?: return
|
||||
val activity = adapter.getActivity(position)
|
||||
DebugLog.v(msg = "Load activity gap $activity")
|
||||
val accountIds = arrayOf(activity.account_key)
|
||||
val maxIds = arrayOf(activity.min_position)
|
||||
val maxSortIds = longArrayOf(activity.min_sort_position)
|
||||
getActivities(BaseRefreshTaskParam(accountKeys = accountIds, maxIds = maxIds,
|
||||
sinceIds = null, maxSortIds = maxSortIds, sinceSortIds = null))
|
||||
sinceIds = null, maxSortIds = maxSortIds, sinceSortIds = null).also {
|
||||
it.extraId = activity._id
|
||||
})
|
||||
}
|
||||
|
||||
override fun onMediaClick(holder: IStatusViewHolder, view: View, media: ParcelableMedia, position: Int) {
|
||||
val status = adapter.getActivity(position)?.getActivityStatus() ?: return
|
||||
val status = adapter.getActivity(position).getActivityStatus() ?: return
|
||||
IntentUtils.openMedia(activity, status, media, preferences[newDocumentApiKey],
|
||||
preferences[displaySensitiveContentsKey],
|
||||
null)
|
||||
|
@ -355,7 +361,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
}
|
||||
|
||||
override fun onActivityClick(holder: ActivityTitleSummaryViewHolder, position: Int) {
|
||||
val activity = adapter.getActivity(position) ?: return
|
||||
val activity = adapter.getActivity(position)
|
||||
val list = ArrayList<Parcelable>()
|
||||
if (activity.target_object_statuses?.isNotEmpty() ?: false) {
|
||||
list.addAll(activity.target_object_statuses)
|
||||
|
@ -387,7 +393,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
}
|
||||
|
||||
private fun getActivityStatus(position: Int): ParcelableStatus? {
|
||||
return adapter.getActivity(position)?.getActivityStatus()
|
||||
return adapter.getActivity(position).getActivityStatus()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -458,7 +464,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
protected fun saveReadPosition(position: Int) {
|
||||
if (host == null) return
|
||||
if (position == RecyclerView.NO_POSITION) return
|
||||
val item = adapter.getActivity(position) ?: return
|
||||
val item = adapter.getActivity(position)
|
||||
var positionUpdated = false
|
||||
readPositionTag?.let {
|
||||
for (accountKey in accountKeys) {
|
||||
|
@ -502,22 +508,33 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
if (!userVisibleHint) return false
|
||||
val contextMenuInfo = item.menuInfo as ExtendedRecyclerView.ContextMenuInfo
|
||||
val position = contextMenuInfo.position
|
||||
|
||||
when (adapter.getItemViewType(position)) {
|
||||
ITEM_VIEW_TYPE_STATUS -> {
|
||||
val status = getActivityStatus(position) ?: return false
|
||||
if (item.itemId == R.id.share) {
|
||||
val shareIntent = Utils.createStatusShareIntent(activity, status)
|
||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_status))
|
||||
startActivity(chooser)
|
||||
when (item.itemId) {
|
||||
R.id.share -> {
|
||||
val shareIntent = Utils.createStatusShareIntent(activity, status)
|
||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_status))
|
||||
startActivity(chooser)
|
||||
|
||||
val am = AccountManager.get(context)
|
||||
val accountType = AccountUtils.findByAccountKey(am, status.account_key)?.getAccountType(am)
|
||||
Analyzer.log(Share.status(accountType, status))
|
||||
return true
|
||||
val am = AccountManager.get(context)
|
||||
val accountType = AccountUtils.findByAccountKey(am, status.account_key)?.getAccountType(am)
|
||||
Analyzer.log(Share.status(accountType, status))
|
||||
return true
|
||||
}
|
||||
R.id.make_gap -> {
|
||||
if (this !is CursorActivitiesFragment) return true
|
||||
val resolver = context.contentResolver
|
||||
val values = ContentValues()
|
||||
values.put(Activities.IS_GAP, 1)
|
||||
val where = Expression.equalsArgs(Activities._ID).sql
|
||||
val whereArgs = arrayOf(adapter.getActivity(position)._id.toString())
|
||||
resolver.update(contentUri, values, where, whereArgs)
|
||||
return true
|
||||
}
|
||||
else -> MenuUtils.handleStatusClick(activity, this, fragmentManager,
|
||||
userColorNameManager, twitterWrapper, status, item)
|
||||
}
|
||||
return MenuUtils.handleStatusClick(activity, this, fragmentManager,
|
||||
userColorNameManager, twitterWrapper, status, item)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.mariotaku.twidere.fragment
|
|||
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Activity
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
|
@ -39,6 +40,7 @@ import edu.tsinghua.hotmobi.model.MediaEvent
|
|||
import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.activity.AccountSelectorActivity
|
||||
import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter
|
||||
|
@ -56,6 +58,8 @@ import org.mariotaku.twidere.model.*
|
|||
import org.mariotaku.twidere.model.analyzer.Share
|
||||
import org.mariotaku.twidere.model.event.StatusListChangedEvent
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
|
||||
import org.mariotaku.twidere.util.glide.PauseRecyclerViewOnScrollListener
|
||||
|
@ -182,7 +186,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
position = recyclerView.getChildLayoutPosition(focusedChild)
|
||||
}
|
||||
if (position != -1) {
|
||||
val status = adapter.getStatus(position) ?: return false
|
||||
val status = adapter.getStatus(position)
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
IntentUtils.openStatus(activity, status, null)
|
||||
return true
|
||||
|
@ -283,7 +287,8 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||
wasAtTop = firstVisibleItemPosition == 0
|
||||
val statusRange = rangeOfSize(adapter.statusStartIndex, adapter.statusCount)
|
||||
// Get display range of statuses
|
||||
val statusRange = rangeOfSize(adapter.statusStartIndex, adapter.getStatusCount(raw = false))
|
||||
val lastReadPosition = if (loadMore || readFromBottom) {
|
||||
lastVisibleItemPosition
|
||||
} else {
|
||||
|
@ -295,7 +300,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
adapter.getStatusPositionKey(lastReadPosition)
|
||||
}
|
||||
lastReadViewTop = layoutManager.findViewByPosition(lastReadPosition)?.top ?: 0
|
||||
loadMore = statusRange.endInclusive >= 0 && lastVisibleItemPosition >= statusRange.endInclusive
|
||||
loadMore = statusRange.endInclusive in 0..lastVisibleItemPosition
|
||||
} else if (rememberPosition && readPositionTag != null) {
|
||||
lastReadId = readStateManager.getPosition(readPositionTag)
|
||||
lastReadViewTop = 0
|
||||
|
@ -351,8 +356,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
|
||||
|
||||
override fun onGapClick(holder: GapViewHolder, position: Int) {
|
||||
val adapter = this.adapter
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
DebugLog.v(msg = "Load activity gap $status")
|
||||
adapter.addGapLoadingId(ObjectId(status.account_key, status.id))
|
||||
val accountIds = arrayOf(status.account_key)
|
||||
|
@ -364,7 +368,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
|
||||
override fun onMediaClick(holder: IStatusViewHolder, view: View, current: ParcelableMedia,
|
||||
statusPosition: Int) {
|
||||
val status = adapter.getStatus(statusPosition) ?: return
|
||||
val status = adapter.getStatus(statusPosition)
|
||||
IntentUtils.openMedia(activity, status, current, preferences[newDocumentApiKey],
|
||||
preferences[displaySensitiveContentsKey])
|
||||
// BEGIN HotMobi
|
||||
|
@ -376,7 +380,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
|
||||
override fun onQuotedMediaClick(holder: IStatusViewHolder, view: View, current: ParcelableMedia,
|
||||
statusPosition: Int) {
|
||||
val status = adapter.getStatus(statusPosition) ?: return
|
||||
val status = adapter.getStatus(statusPosition)
|
||||
val quotedMedia = status.quoted_media ?: return
|
||||
IntentUtils.openMedia(activity, status.account_key, status.is_possibly_sensitive, status,
|
||||
current, quotedMedia, preferences[newDocumentApiKey], preferences[displaySensitiveContentsKey])
|
||||
|
@ -388,12 +392,12 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
}
|
||||
|
||||
override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
handleActionClick(holder as StatusViewHolder, status, id)
|
||||
}
|
||||
|
||||
override fun onItemActionLongClick(holder: RecyclerView.ViewHolder, id: Int, position: Int): Boolean {
|
||||
val status = adapter.getStatus(position) ?: return false
|
||||
val status = adapter.getStatus(position)
|
||||
return handleActionLongClick(this, status, adapter.getItemId(position), id)
|
||||
}
|
||||
|
||||
|
@ -428,12 +432,12 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
}
|
||||
|
||||
override fun onStatusClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
IntentUtils.openStatus(activity, status, null)
|
||||
}
|
||||
|
||||
override fun onQuotedStatusClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
val quotedId = status.quoted_id ?: return
|
||||
IntentUtils.openStatus(activity, status.account_key, quotedId)
|
||||
}
|
||||
|
@ -450,7 +454,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
}
|
||||
|
||||
override fun onUserProfileClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position)!!
|
||||
val status = adapter.getStatus(position)
|
||||
val intent = IntentUtils.userProfile(status.account_key, status.user_key,
|
||||
status.user_screen_name, Referral.TIMELINE_STATUS,
|
||||
status.extras.user_statusnet_profile_url)
|
||||
|
@ -482,7 +486,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
protected fun saveReadPosition(position: Int) {
|
||||
if (host == null) return
|
||||
if (position == RecyclerView.NO_POSITION) return
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
val positionKey = if (status.position_key > 0) status.position_key else status.timestamp
|
||||
readPositionTagWithArguments?.let {
|
||||
accountKeys.map { accountKey -> Utils.getReadPositionTagWithAccount(it, accountKey) }
|
||||
|
@ -508,26 +512,38 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
val contextMenuInfo = menuInfo as ExtendedRecyclerView.ContextMenuInfo?
|
||||
val status = adapter.getStatus(contextMenuInfo!!.position)
|
||||
inflater.inflate(R.menu.action_status, menu)
|
||||
MenuUtils.setupForStatus(context, preferences, menu, status!!, twitterWrapper,
|
||||
MenuUtils.setupForStatus(context, preferences, menu, status, twitterWrapper,
|
||||
userColorNameManager)
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
if (!userVisibleHint) return false
|
||||
val contextMenuInfo = item.menuInfo as ExtendedRecyclerView.ContextMenuInfo
|
||||
val status = adapter.getStatus(contextMenuInfo.position) ?: return false
|
||||
if (item.itemId == R.id.share) {
|
||||
val shareIntent = Utils.createStatusShareIntent(activity, status)
|
||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_status))
|
||||
startActivity(chooser)
|
||||
val status = adapter.getStatus(contextMenuInfo.position)
|
||||
when (item.itemId) {
|
||||
R.id.share -> {
|
||||
val shareIntent = Utils.createStatusShareIntent(activity, status)
|
||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_status))
|
||||
startActivity(chooser)
|
||||
|
||||
val am = AccountManager.get(context)
|
||||
val accountType = AccountUtils.findByAccountKey(am, status.account_key)?.getAccountType(am)
|
||||
Analyzer.log(Share.status(accountType, status))
|
||||
return true
|
||||
val am = AccountManager.get(context)
|
||||
val accountType = AccountUtils.findByAccountKey(am, status.account_key)?.getAccountType(am)
|
||||
Analyzer.log(Share.status(accountType, status))
|
||||
return true
|
||||
}
|
||||
R.id.make_gap -> {
|
||||
if (this !is CursorStatusesFragment) return true
|
||||
val resolver = context.contentResolver
|
||||
val values = ContentValues()
|
||||
values.put(Statuses.IS_GAP, 1)
|
||||
val where = Expression.equalsArgs(Statuses._ID).sql
|
||||
val whereArgs = arrayOf(status._id.toString())
|
||||
resolver.update(contentUri, values, where, whereArgs)
|
||||
return true
|
||||
}
|
||||
else -> return MenuUtils.handleStatusClick(activity, this, fragmentManager,
|
||||
userColorNameManager, twitterWrapper, status, item)
|
||||
}
|
||||
return MenuUtils.handleStatusClick(activity, this, fragmentManager,
|
||||
userColorNameManager, twitterWrapper, status, item)
|
||||
}
|
||||
|
||||
class DefaultOnLikedListener(
|
||||
|
|
|
@ -269,10 +269,10 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
if (result == null) return
|
||||
val lm = layoutManager
|
||||
val rangeStart = Math.max(adapter.activityStartIndex, lm.findFirstVisibleItemPosition())
|
||||
val rangeEnd = Math.min(lm.findLastVisibleItemPosition(), adapter.activityStartIndex + adapter.activityCount - 1)
|
||||
val rangeEnd = Math.min(lm.findLastVisibleItemPosition(), adapter.activityStartIndex + adapter.getActivityCount(false) - 1)
|
||||
loop@ for (i in rangeStart..rangeEnd) {
|
||||
val activity = adapter.getActivity(i)
|
||||
if (result.account_key == activity!!.account_key && result.id == activity.status_id) {
|
||||
val activity = adapter.getActivity(i, false)
|
||||
if (result.account_key == activity.account_key && result.id == activity.status_id) {
|
||||
if (result.id != activity.status_id) {
|
||||
continue@loop
|
||||
}
|
||||
|
|
|
@ -152,9 +152,11 @@ abstract class ParcelableStatusesFragment : AbsStatusesFragment() {
|
|||
super.onLoadMoreContents(position)
|
||||
if (position == 0L) return
|
||||
// Load the last item
|
||||
val idx = adapter.rawStatusCount - 1
|
||||
if (idx < 0) return
|
||||
val status = adapter.getData()?.get(idx) ?: return
|
||||
val startIdx = adapter.statusStartIndex
|
||||
if (startIdx < 0) return
|
||||
val statusCount = adapter.getStatusCount(true)
|
||||
if (statusCount <= 0) return
|
||||
val status = adapter.getStatus(startIdx + statusCount - 1, true)
|
||||
val accountKeys = arrayOf(status.account_key)
|
||||
val maxIds = arrayOf<String?>(status.id)
|
||||
val param = StatusesRefreshTaskParam(accountKeys, maxIds, null, page + pageDelta)
|
||||
|
@ -166,9 +168,9 @@ abstract class ParcelableStatusesFragment : AbsStatusesFragment() {
|
|||
if (status == null) return
|
||||
val lm = layoutManager
|
||||
val rangeStart = Math.max(adapter.statusStartIndex, lm.findFirstVisibleItemPosition())
|
||||
val rangeEnd = Math.min(lm.findLastVisibleItemPosition(), adapter.statusStartIndex + adapter.statusCount - 1)
|
||||
val rangeEnd = Math.min(lm.findLastVisibleItemPosition(), adapter.statusStartIndex + adapter.getStatusCount(false) - 1)
|
||||
for (i in rangeStart..rangeEnd) {
|
||||
val item = adapter.getStatus(i)
|
||||
val item = adapter.getStatus(i, false)
|
||||
if (status == item) {
|
||||
item.favorite_count = status.favorite_count
|
||||
item.retweet_count = status.retweet_count
|
||||
|
@ -183,8 +185,8 @@ abstract class ParcelableStatusesFragment : AbsStatusesFragment() {
|
|||
override fun triggerRefresh(): Boolean {
|
||||
super.triggerRefresh()
|
||||
val accountKeys = accountKeys
|
||||
if (adapter.statusCount > 0) {
|
||||
val firstStatus = adapter.getStatus(0)!!
|
||||
if (adapter.getStatusCount(true) > 0) {
|
||||
val firstStatus = adapter.getStatus(0, true)
|
||||
val sinceIds = Array(accountKeys.size) {
|
||||
return@Array if (firstStatus.account_key == accountKeys[it]) firstStatus.id else null
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ import org.mariotaku.twidere.adapter.ListParcelableStatusesAdapter
|
|||
import org.mariotaku.twidere.adapter.LoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition
|
||||
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
|
||||
|
@ -303,7 +304,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
}
|
||||
|
||||
override fun onMediaClick(holder: IStatusViewHolder, view: View, current: ParcelableMedia, statusPosition: Int) {
|
||||
val status = adapter.getStatus(statusPosition) ?: return
|
||||
val status = adapter.getStatus(statusPosition)
|
||||
IntentUtils.openMedia(activity, status, current, preferences[newDocumentApiKey],
|
||||
preferences[displaySensitiveContentsKey])
|
||||
|
||||
|
@ -317,23 +318,23 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
}
|
||||
|
||||
override fun onItemActionClick(holder: ViewHolder, id: Int, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
handleActionClick(holder as StatusViewHolder, status, id)
|
||||
}
|
||||
|
||||
|
||||
override fun onItemActionLongClick(holder: RecyclerView.ViewHolder, id: Int, position: Int): Boolean {
|
||||
val status = adapter.getStatus(position) ?: return false
|
||||
val status = adapter.getStatus(position)
|
||||
return AbsStatusesFragment.handleActionLongClick(this, status, adapter.getItemId(position), id)
|
||||
}
|
||||
|
||||
override fun onStatusClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
IntentUtils.openStatus(activity, status)
|
||||
}
|
||||
|
||||
override fun onQuotedStatusClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
val status = adapter.getStatus(position)
|
||||
val quotedId = status.quoted_id ?: return
|
||||
IntentUtils.openStatus(activity, status.account_key, quotedId)
|
||||
}
|
||||
|
@ -349,7 +350,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
}
|
||||
|
||||
override fun onUserProfileClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position)!!
|
||||
val status = adapter.getStatus(position)
|
||||
IntentUtils.openUserProfile(activity, status.account_key, status.user_key,
|
||||
status.user_screen_name, preferences[newDocumentApiKey], Referral.TIMELINE_STATUS,
|
||||
null)
|
||||
|
@ -378,7 +379,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
return false
|
||||
}
|
||||
if (position == -1) return false
|
||||
val status = adapter.getStatus(position) ?: return false
|
||||
val status = adapter.getStatus(position)
|
||||
val action = handler.getKeyAction(CONTEXT_TAG_STATUS, keyCode, event, metaState) ?: return false
|
||||
when (action) {
|
||||
ACTION_STATUS_REPLY -> {
|
||||
|
@ -520,11 +521,11 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
if (position and ILoadMoreSupportAdapter.START != 0L) {
|
||||
val start = adapter.getIndexStart(StatusAdapter.ITEM_IDX_CONVERSATION)
|
||||
val status = adapter.getStatus(start)
|
||||
if (status == null || status.in_reply_to_status_id == null) return
|
||||
if (status.in_reply_to_status_id == null) return
|
||||
loadConversation(status, null, status.id)
|
||||
} else if (position and ILoadMoreSupportAdapter.END != 0L) {
|
||||
val start = adapter.getIndexStart(StatusAdapter.ITEM_IDX_CONVERSATION)
|
||||
val status = adapter.getStatus(start + adapter.statusCount - 1) ?: return
|
||||
val status = adapter.getStatus(start + adapter.getStatusCount(true) - 1)
|
||||
loadConversation(status, status.id, null)
|
||||
}
|
||||
adapter.loadMoreIndicatorPosition = position
|
||||
|
@ -663,7 +664,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
if (!userVisibleHint) return
|
||||
val contextMenuInfo = menuInfo as? ExtendedRecyclerView.ContextMenuInfo ?: return
|
||||
val status = adapter.getStatus(contextMenuInfo.position) ?: return
|
||||
val status = adapter.getStatus(contextMenuInfo.position)
|
||||
val inflater = MenuInflater(context)
|
||||
inflater.inflate(R.menu.action_status, menu)
|
||||
MenuUtils.setupForStatus(context, preferences, menu, status, twitterWrapper,
|
||||
|
@ -673,7 +674,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
if (!userVisibleHint) return false
|
||||
val contextMenuInfo = item.menuInfo as? ExtendedRecyclerView.ContextMenuInfo ?: return false
|
||||
val status = adapter.getStatus(contextMenuInfo.position) ?: return false
|
||||
val status = adapter.getStatus(contextMenuInfo.position)
|
||||
if (item.itemId == R.id.share) {
|
||||
val shareIntent = Utils.createStatusShareIntent(activity, status)
|
||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_status))
|
||||
|
@ -1084,7 +1085,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val status = adapter.getStatus(layoutPosition) ?: return
|
||||
val status = adapter.getStatus(layoutPosition)
|
||||
val fragment = adapter.fragment
|
||||
val preferences = fragment.preferences
|
||||
when (v) {
|
||||
|
@ -1129,7 +1130,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
val layoutPosition = layoutPosition
|
||||
if (layoutPosition < 0) return false
|
||||
val fragment = adapter.fragment
|
||||
val status = adapter.getStatus(layoutPosition) ?: return false
|
||||
val status = adapter.getStatus(layoutPosition)
|
||||
val twitter = fragment.twitterWrapper
|
||||
val manager = fragment.userColorNameManager
|
||||
val activity = fragment.activity
|
||||
|
@ -1488,7 +1489,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
|
||||
class StatusAdapter(
|
||||
val fragment: StatusFragment
|
||||
) : LoadMoreSupportAdapter<ViewHolder>(fragment.context, Glide.with(fragment)), IStatusesAdapter<List<ParcelableStatus>> {
|
||||
) : LoadMoreSupportAdapter<ViewHolder>(fragment.context, Glide.with(fragment)),
|
||||
IStatusesAdapter<List<ParcelableStatus>>, IItemCountsAdapter {
|
||||
private val inflater: LayoutInflater
|
||||
override val twidereLinkify: TwidereLinkify
|
||||
|
||||
|
@ -1496,7 +1498,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
private var recyclerView: RecyclerView? = null
|
||||
private var statusViewHolder: DetailStatusViewHolder? = null
|
||||
|
||||
private val itemCounts = ItemCounts(ITEM_TYPES_SUM)
|
||||
override val itemCounts = ItemCounts(ITEM_TYPES_SUM)
|
||||
|
||||
private val cardBackgroundColor: Int
|
||||
override val nameFirst = preferences[nameFirstKey]
|
||||
|
@ -1555,22 +1557,19 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
twidereLinkify = TwidereLinkify(listener)
|
||||
}
|
||||
|
||||
override fun getStatus(position: Int): ParcelableStatus? {
|
||||
val itemType = getItemType(position)
|
||||
when (itemType) {
|
||||
override fun getStatus(position: Int, raw: Boolean): ParcelableStatus {
|
||||
when (getItemCountIndex(position, raw)) {
|
||||
ITEM_IDX_CONVERSATION -> {
|
||||
return data?.get(position - getIndexStart(ITEM_IDX_CONVERSATION))
|
||||
return data!![position - getIndexStart(ITEM_IDX_CONVERSATION)]
|
||||
}
|
||||
ITEM_IDX_REPLY -> {
|
||||
if (replyStart < 0) return null
|
||||
return data?.get(position - getIndexStart(ITEM_IDX_CONVERSATION)
|
||||
- getTypeCount(ITEM_IDX_CONVERSATION) - getTypeCount(ITEM_IDX_STATUS) + replyStart)
|
||||
return data!![position - getIndexStart(ITEM_IDX_CONVERSATION) - getTypeCount(ITEM_IDX_CONVERSATION) - getTypeCount(ITEM_IDX_STATUS) + replyStart]
|
||||
}
|
||||
ITEM_IDX_STATUS -> {
|
||||
return status
|
||||
return status!!
|
||||
}
|
||||
}
|
||||
return null
|
||||
throw IndexOutOfBoundsException("index: $position")
|
||||
}
|
||||
|
||||
fun getIndexStart(index: Int): Int {
|
||||
|
@ -1578,25 +1577,20 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
return itemCounts.getItemStartPosition(index)
|
||||
}
|
||||
|
||||
override fun getStatusId(position: Int): String? {
|
||||
val status = getStatus(position)
|
||||
return status?.id
|
||||
override fun getStatusId(position: Int, raw: Boolean): String {
|
||||
return getStatus(position, raw).id
|
||||
}
|
||||
|
||||
override fun getStatusTimestamp(position: Int): Long {
|
||||
val status = getStatus(position)
|
||||
return status?.timestamp ?: -1
|
||||
override fun getStatusTimestamp(position: Int, raw: Boolean): Long {
|
||||
return getStatus(position, raw).timestamp
|
||||
}
|
||||
|
||||
override fun getStatusPositionKey(position: Int): Long {
|
||||
val status = getStatus(position) ?: return -1
|
||||
return if (status.position_key > 0) status.timestamp else getStatusTimestamp(position)
|
||||
override fun getStatusPositionKey(position: Int, raw: Boolean): Long {
|
||||
val status = getStatus(position, raw)
|
||||
return if (status.position_key > 0) status.timestamp else getStatusTimestamp(position, raw)
|
||||
}
|
||||
|
||||
override fun getAccountKey(position: Int): UserKey? {
|
||||
val status = getStatus(position)
|
||||
return status?.account_key
|
||||
}
|
||||
override fun getAccountKey(position: Int, raw: Boolean) = getStatus(position, raw).account_key
|
||||
|
||||
override fun findStatusById(accountKey: UserKey, statusId: String): ParcelableStatus? {
|
||||
if (status != null && accountKey == status!!.account_key && TextUtils.equals(statusId, status!!.id)) {
|
||||
|
@ -1605,13 +1599,9 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
return data?.firstOrNull { accountKey == it.account_key && TextUtils.equals(it.id, statusId) }
|
||||
}
|
||||
|
||||
override val statusCount: Int
|
||||
get() = rawStatusCount
|
||||
|
||||
override val rawStatusCount: Int
|
||||
get() {
|
||||
return getTypeCount(ITEM_IDX_CONVERSATION) + getTypeCount(ITEM_IDX_STATUS) + getTypeCount(ITEM_IDX_REPLY)
|
||||
}
|
||||
override fun getStatusCount(raw: Boolean): Int {
|
||||
return getTypeCount(ITEM_IDX_CONVERSATION) + getTypeCount(ITEM_IDX_STATUS) + getTypeCount(ITEM_IDX_REPLY)
|
||||
}
|
||||
|
||||
override fun isCardActionsShown(position: Int): Boolean {
|
||||
if (position == RecyclerView.NO_POSITION) return showCardActions
|
||||
|
@ -1732,8 +1722,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
VIEW_TYPE_DETAIL_STATUS -> {
|
||||
val status = getStatus(position)
|
||||
val detailHolder = holder as DetailStatusViewHolder
|
||||
detailHolder.displayStatus(statusAccount, status, statusActivity,
|
||||
translationResult)
|
||||
detailHolder.displayStatus(statusAccount, status, statusActivity, translationResult)
|
||||
}
|
||||
VIEW_TYPE_LIST_STATUS -> {
|
||||
val status = getStatus(position)
|
||||
|
@ -1743,7 +1732,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
// We only display that indicator for first conversation item
|
||||
val itemType = getItemType(position)
|
||||
val displayInReplyTo = itemType == ITEM_IDX_CONVERSATION && position - getItemTypeStart(position) == 0
|
||||
statusHolder.displayStatus(status = status!!, displayInReplyTo = displayInReplyTo)
|
||||
statusHolder.displayStatus(status = status, displayInReplyTo = displayInReplyTo)
|
||||
}
|
||||
VIEW_TYPE_REPLY_ERROR -> {
|
||||
val errorHolder = holder as StatusErrorItemViewHolder
|
||||
|
@ -1803,12 +1792,16 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun getItemCountIndex(position: Int, raw: Boolean): Int {
|
||||
return itemCounts.getItemCountIndex(position)
|
||||
}
|
||||
|
||||
fun getItemType(position: Int): Int {
|
||||
var typeStart = 0
|
||||
for (type in 0..ITEM_TYPES_SUM - 1) {
|
||||
val typeCount = getTypeCount(type)
|
||||
val typeEnd = typeStart + typeCount
|
||||
if (position >= typeStart && position < typeEnd) return type
|
||||
if (position in typeStart until typeEnd) return type
|
||||
typeStart = typeEnd
|
||||
}
|
||||
throw IllegalStateException("Unknown position " + position)
|
||||
|
@ -1819,15 +1812,18 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
for (type in 0..ITEM_TYPES_SUM - 1) {
|
||||
val typeCount = getTypeCount(type)
|
||||
val typeEnd = typeStart + typeCount
|
||||
if (position >= typeStart && position < typeEnd) return typeStart
|
||||
if (position in typeStart until typeEnd) return typeStart
|
||||
typeStart = typeEnd
|
||||
}
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val status = getStatus(position)
|
||||
if (status != null) return status.hashCode().toLong()
|
||||
when (getItemCountIndex(position)) {
|
||||
ITEM_IDX_CONVERSATION, ITEM_IDX_STATUS, ITEM_IDX_REPLY -> {
|
||||
return getStatus(position).hashCode().toLong()
|
||||
}
|
||||
}
|
||||
return getItemType(position).toLong()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.support.v7.widget.RecyclerView
|
|||
import android.support.v7.widget.StaggeredGridLayoutManager
|
||||
import android.text.TextUtils
|
||||
import com.bumptech.glide.Glide
|
||||
import org.mariotaku.ktextension.contains
|
||||
import org.mariotaku.twidere.adapter.StaggeredGridParcelableStatusesAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.constant.IntentConstants.*
|
||||
|
@ -119,11 +118,11 @@ class UserMediaTimelineFragment : AbsContentRecyclerViewFragment<StaggeredGridPa
|
|||
}
|
||||
|
||||
override fun onLoadMoreContents(position: Long) {
|
||||
// Only supports load from end, skip START flag
|
||||
if (ILoadMoreSupportAdapter.START in position) return
|
||||
// Only supports load from end
|
||||
if (ILoadMoreSupportAdapter.END != position) return
|
||||
super.onLoadMoreContents(position)
|
||||
if (position == 0L) return
|
||||
val maxId = adapter.getStatusId(adapter.statusCount - 1)
|
||||
// Get last raw status
|
||||
val maxId = adapter.getStatusId(adapter.statusStartIndex + adapter.getStatusCount(true) - 1)
|
||||
getStatuses(maxId, null)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ open class BaseRefreshTaskParam(
|
|||
override val maxSortIds: LongArray? = null,
|
||||
override val sinceSortIds: LongArray? = null
|
||||
) : RefreshTaskParam {
|
||||
override var extraId: Long = -1L
|
||||
override var isLoadingMore: Boolean = false
|
||||
override var shouldAbort: Boolean = false
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ interface RefreshTaskParam {
|
|||
val hasCursors: Boolean
|
||||
get() = cursors != null
|
||||
|
||||
val extraId: Long
|
||||
|
||||
val isLoadingMore: Boolean
|
||||
|
||||
val shouldAbort: Boolean
|
||||
|
|
|
@ -22,6 +22,9 @@ abstract class SimpleRefreshTaskParam : RefreshTaskParam {
|
|||
override val maxSortIds: LongArray?
|
||||
get() = null
|
||||
|
||||
override val extraId: Long
|
||||
get() = -1
|
||||
|
||||
override val isLoadingMore: Boolean
|
||||
get() = false
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@ import android.support.v4.util.SimpleArrayMap
|
|||
import android.util.Log
|
||||
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUserStream
|
||||
import org.mariotaku.microblog.library.twitter.UserStreamCallback
|
||||
import org.mariotaku.microblog.library.twitter.model.*
|
||||
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.R
|
||||
|
@ -33,9 +35,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.*
|
|||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.TwidereArrayUtils
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class StreamingService : Service() {
|
||||
|
||||
|
@ -153,152 +153,83 @@ class StreamingService : Service() {
|
|||
private var statusStreamStarted: Boolean = false
|
||||
private val mentionsStreamStarted: Boolean = false
|
||||
|
||||
override fun onConnected() {
|
||||
override fun onConnected() = true
|
||||
|
||||
}
|
||||
|
||||
override fun onBlock(source: User, blockedUser: User) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun onDirectMessage(directMessage: DirectMessage) {
|
||||
if (directMessage.id == null) return
|
||||
|
||||
}
|
||||
|
||||
override fun onException(ex: Throwable) {
|
||||
if (ex is MicroBlogException) {
|
||||
Log.w(LOGTAG, String.format("Error %d", ex.statusCode), ex)
|
||||
val response = ex.httpResponse
|
||||
if (response != null) {
|
||||
try {
|
||||
val body = response.body
|
||||
if (body != null) {
|
||||
val os = ByteArrayOutputStream()
|
||||
body.writeTo(os)
|
||||
val charsetName: String
|
||||
val contentType = body.contentType()
|
||||
if (contentType != null) {
|
||||
val charset = contentType.charset
|
||||
if (charset != null) {
|
||||
charsetName = charset.name()
|
||||
} else {
|
||||
charsetName = Charset.defaultCharset().name()
|
||||
}
|
||||
} else {
|
||||
charsetName = Charset.defaultCharset().name()
|
||||
}
|
||||
Log.w(LOGTAG, os.toString(charsetName))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.w(LOGTAG, e)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Log.w(LOGTAG, ex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavorite(source: User, target: User, targetStatus: Status) {
|
||||
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) {
|
||||
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: LongArray) {
|
||||
|
||||
override fun onFriendList(friendIds: Array<String>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScrubGeo(userId: Long, upToStatusId: Long) {
|
||||
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.toString(), upToStatusId.toString())
|
||||
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) {
|
||||
|
||||
override fun onStallWarning(warn: Warning): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun onStatus(status: Status) {
|
||||
|
||||
override fun onStatus(status: Status): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUnblock(source: User, unblockedUser: User) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
override fun onUserListCreation(listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListDeletion(listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListMemberAddition(addedMember: User, listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListMemberDeletion(deletedMember: User, listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListSubscription(subscriber: User, listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListUnsubscription(subscriber: User, listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserListUpdate(listOwner: User, list: UserList) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUserProfileUpdate(updatedUser: User) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -168,7 +168,9 @@ abstract class GetActivitiesTask(
|
|||
Expression.greaterEqualsArgs(Activities.MIN_SORT_POSITION),
|
||||
Expression.lesserEqualsArgs(Activities.MAX_SORT_POSITION))
|
||||
val whereArgs = arrayOf(details.key.toString(), deleteBound[0].toString(), deleteBound[1].toString())
|
||||
val rowsDeleted = cr.delete(writeUri, where.sql, whereArgs)
|
||||
// First item after gap doesn't count
|
||||
val localDeleted = if (maxId != null && sinceId == null) 1 else 0
|
||||
val rowsDeleted = cr.delete(writeUri, where.sql, whereArgs) - localDeleted
|
||||
// Why loadItemLimit / 2? because it will not acting strange in most cases
|
||||
val insertGap = !noItemsBefore && olderCount > 0 && rowsDeleted <= 0 && activities.size > loadItemLimit / 2
|
||||
if (insertGap && !valuesList.isEmpty()) {
|
||||
|
@ -182,13 +184,13 @@ abstract class GetActivitiesTask(
|
|||
if (maxId != null && sinceId == null) {
|
||||
if (activities.isNotEmpty()) {
|
||||
// Only remove when actual result returned, otherwise it seems that gap is too old to load
|
||||
val noGapValues = ContentValues()
|
||||
noGapValues.put(Activities.IS_GAP, false)
|
||||
val noGapWhere = Expression.and(Expression.equalsArgs(Activities.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Activities.MIN_REQUEST_POSITION),
|
||||
Expression.equalsArgs(Activities.MAX_REQUEST_POSITION)).sql
|
||||
val noGapWhereArgs = arrayOf(details.key.toString(), maxId, maxId)
|
||||
cr.update(writeUri, noGapValues, noGapWhere, noGapWhereArgs)
|
||||
if (params.extraId != -1L) {
|
||||
val noGapValues = ContentValues()
|
||||
noGapValues.put(Activities.IS_GAP, false)
|
||||
val noGapWhere = Expression.equalsArgs(Activities._ID).sql
|
||||
val noGapWhereArgs = arrayOf(params.extraId.toString())
|
||||
cr.update(writeUri, noGapValues, noGapWhere, noGapWhereArgs)
|
||||
}
|
||||
} else {
|
||||
return GetStatusesTask.ERROR_LOAD_GAP
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ object MenuUtils {
|
|||
item.setTitle(icon)
|
||||
}
|
||||
|
||||
@JvmOverloads fun addIntentToMenu(context: Context?, menu: Menu?, queryIntent: Intent?,
|
||||
fun addIntentToMenu(context: Context?, menu: Menu?, queryIntent: Intent?,
|
||||
groupId: Int = Menu.NONE) {
|
||||
if (context == null || menu == null || queryIntent == null) return
|
||||
val pm = context.packageManager
|
||||
|
@ -330,14 +330,6 @@ object MenuUtils {
|
|||
ClipboardUtils.setText(context, uri.toString())
|
||||
Utils.showOkMessage(context, R.string.message_toast_link_copied_to_clipboard, false)
|
||||
}
|
||||
R.id.make_gap -> {
|
||||
val resolver = context.contentResolver
|
||||
val values = ContentValues()
|
||||
values.put(Statuses.IS_GAP, 1)
|
||||
val where = Expression.equalsArgs(Statuses._ID).sql
|
||||
val whereArgs = arrayOf(status._id.toString())
|
||||
resolver.update(Statuses.CONTENT_URI, values, where, whereArgs)
|
||||
}
|
||||
R.id.mute_users -> {
|
||||
val df = MuteStatusUsersDialogFragment()
|
||||
df.arguments = Bundle {
|
||||
|
|
Loading…
Reference in New Issue