diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java index 1b424a8a6..b63022a45 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java @@ -64,6 +64,7 @@ public interface TwidereDataStore { CachedHashtags.CONTENT_URI, CachedTrends.Local.CONTENT_URI}; Uri[] DIRECT_MESSAGES_URIS = new Uri[]{DirectMessages.Inbox.CONTENT_URI, DirectMessages.Outbox.CONTENT_URI}; + Uri[] ACTIVITIES_URIS = new Uri[]{Activities.AboutMe.CONTENT_URI}; interface InsertedDateColumns { String INSERTED_DATE = "inserted_date"; diff --git a/twidere/build.gradle b/twidere/build.gradle index 3064385f9..88df4b319 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -111,7 +111,7 @@ dependencies { compile 'org.attoparser:attoparser:1.4.0.RELEASE' compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10' compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10' - compile 'com.github.mariotaku:SQLiteQB:0.9.4' + compile 'com.github.mariotaku.SQLiteQB:library:0.9.5-SNAPSHOT' compile 'com.github.mariotaku.ObjectCursor:core:0.9.4' compile project(':twidere.component.common') compile project(':twidere.component.nyan') diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java index fb7425259..0d3e6343a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java @@ -82,6 +82,7 @@ import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.SupportTabSpec; import org.mariotaku.twidere.model.message.TaskStateChangedEvent; import org.mariotaku.twidere.model.message.UnreadCountUpdatedEvent; +import org.mariotaku.twidere.provider.TwidereDataStore; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Activities; import org.mariotaku.twidere.provider.TwidereDataStore.Statuses; @@ -917,7 +918,8 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen } } - private static class UpdateUnreadCountTask extends AsyncTask { + private static class UpdateUnreadCountTask extends AsyncTask { private final Context mContext; private final ReadStateManager mReadStateManager; private final TabPagerIndicator mIndicator; @@ -934,17 +936,22 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen @Override protected SparseIntArray doInBackground(final Object... params) { final SparseIntArray result = new SparseIntArray(); - for (int i = 0, count = mTabs.size(); i < count; i++) { + for (int i = 0, j = mTabs.size(); i < j; i++) { SupportTabSpec spec = mTabs.get(i); - if (spec.type == null) continue; + if (spec.type == null) { + publishProgress(new TabBadge(i, -1)); + continue; + } switch (spec.type) { case CustomTabType.HOME_TIMELINE: { final long[] accountIds = Utils.getAccountIds(spec.args); final String tagWithAccounts = Utils.getReadPositionTagWithAccounts(mContext, true, spec.tag, accountIds); final long position = mReadStateManager.getPosition(tagWithAccounts); - result.put(i, DataStoreUtils.getStatusesCount(mContext, Statuses.CONTENT_URI, - position, accountIds)); + final int count = DataStoreUtils.getStatusesCount(mContext, Statuses.CONTENT_URI, + position, accountIds); + result.put(i, count); + publishProgress(new TabBadge(i, count)); break; } case CustomTabType.NOTIFICATIONS_TIMELINE: { @@ -955,20 +962,29 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen Expression extraWhere = null; String[] extraWhereArgs = null; + boolean followingOnly = false; if (spec.args != null) { Bundle extras = spec.args.getBundle(EXTRA_EXTRAS); - if (extras != null && extras.getBoolean(EXTRA_MENTIONS_ONLY)) { - extraWhere = Expression.inArgs(Activities.ACTION, 3); - extraWhereArgs = new String[]{Activity.Action.MENTION, - Activity.Action.REPLY, Activity.Action.QUOTE}; + if (extras != null) { + if (extras.getBoolean(EXTRA_MENTIONS_ONLY)) { + extraWhere = Expression.inArgs(Activities.ACTION, 3); + extraWhereArgs = new String[]{Activity.Action.MENTION, + Activity.Action.REPLY, Activity.Action.QUOTE}; + } + if (extras.getBoolean(EXTRA_MY_FOLLOWING_ONLY)) { + followingOnly = true; + } } } - result.put(i, DataStoreUtils.getActivitiesCount(mContext, + final int count = DataStoreUtils.getActivitiesCount(mContext, Activities.AboutMe.CONTENT_URI, extraWhere, extraWhereArgs, - position, accountIds)); + position, followingOnly, accountIds); + publishProgress(new TabBadge(i, count)); + result.put(i, count); break; } - case CustomTabType.DIRECT_MESSAGES: { + default: { + publishProgress(new TabBadge(i, -1)); break; } } @@ -984,6 +1000,22 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen } } + @Override + protected void onProgressUpdate(TabBadge... values) { + for (TabBadge value : values) { + mIndicator.setBadge(value.index, value.count); + } + } + + static class TabBadge { + int index; + int count; + + public TabBadge(int index, int count) { + this.index = index; + this.count = count; + } + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/UserFollowState.java b/twidere/src/main/java/org/mariotaku/twidere/model/UserFollowState.java new file mode 100644 index 000000000..ebc8da54f --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/model/UserFollowState.java @@ -0,0 +1,24 @@ +package org.mariotaku.twidere.model; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease; + +import org.mariotaku.library.objectcursor.annotation.CursorField; +import org.mariotaku.twidere.provider.TwidereDataStore; + +/** + * Created by mariotaku on 16/2/25. + * + * This is a subset of {@link ParcelableUser}, which only has two fields, id and is_following, + * to minimize processing speed. + */ +@JsonObject +public class UserFollowState { + + @JsonField(name = "id") + public long id; + + @JsonField(name = "is_following") + public boolean is_following; +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/DataStoreUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/DataStoreUtils.java index 25bc1e730..aad86e6f7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/DataStoreUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/DataStoreUtils.java @@ -29,6 +29,9 @@ import android.provider.BaseColumns; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.LongSparseArray; +import android.text.TextUtils; + +import com.bluelinelabs.logansquare.JsonMapper; import org.apache.commons.lang3.ArrayUtils; import org.mariotaku.sqliteqb.library.ArgsArray; @@ -48,6 +51,7 @@ import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableAccountCursorIndices; import org.mariotaku.twidere.model.ParcelableCredentials; import org.mariotaku.twidere.model.ParcelableCredentialsCursorIndices; +import org.mariotaku.twidere.model.UserFollowState; import org.mariotaku.twidere.provider.TwidereDataStore; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Activities; @@ -72,12 +76,14 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions; import org.mariotaku.twidere.provider.TwidereDataStore.Tabs; import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static android.text.TextUtils.isEmpty; +import static org.mariotaku.twidere.provider.TwidereDataStore.ACTIVITIES_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.CACHE_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.DIRECT_MESSAGES_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.STATUSES_URIS; @@ -395,7 +401,7 @@ public class DataStoreUtils implements Constants { public static int getActivitiesCount(final Context context, final Uri uri, final Expression extraWhere, final String[] extraWhereArgs, - final long sinceTimestamp, final long... accountIds) { + final long sinceTimestamp, boolean followingOnly, final long... accountIds) { if (context == null) return 0; final RawItemArray idsIn; if (accountIds == null || accountIds.length == 0 || (accountIds.length == 1 && accountIds[0] < 0)) { @@ -414,6 +420,45 @@ public class DataStoreUtils implements Constants { expressions[1] = Expression.greaterThan(Activities.TIMESTAMP, sinceTimestamp); expressions[2] = buildActivityFilterWhereClause(getTableNameByUri(uri), null); final Expression selection = Expression.and(expressions); + // If followingOnly option is on, we have to iterate over items + if (followingOnly) { + final ContentResolver resolver = context.getContentResolver(); + final String[] projection = new String[]{Activities.SOURCES}; + final Cursor cur = resolver.query(uri, projection, selection.getSQL(), extraWhereArgs, null); + if (cur == null) return -1; + try { + final JsonMapper mapper; + try { + mapper = LoganSquareMapperFinder.mapperFor(UserFollowState.class); + } catch (LoganSquareMapperFinder.ClassLoaderDeadLockException e) { + return -1; + } + int total = 0; + cur.moveToFirst(); + while (cur.isAfterLast()) { + final String string = cur.getString(0); + if (TextUtils.isEmpty(string)) continue; + boolean hasFollowing = false; + try { + for (UserFollowState state : mapper.parseList(string)) { + if (state.is_following) { + hasFollowing = true; + break; + } + } + } catch (IOException e) { + continue; + } + if (hasFollowing) { + total++; + } + cur.moveToNext(); + } + return total; + } finally { + cur.close(); + } + } return queryCount(context, uri, selection.getSQL(), extraWhereArgs); } @@ -648,24 +693,41 @@ public class DataStoreUtils implements Constants { continue; } final String table = getTableNameByUri(uri); - final Expression account_where = new Expression(Statuses.ACCOUNT_ID + " = " + accountId); + final Expression accountWhere = new Expression(Statuses.ACCOUNT_ID + " = " + accountId); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); - qb.select(new Column(Statuses._ID)).from(new Tables(table)); - qb.where(Expression.equals(Statuses.ACCOUNT_ID, accountId)); - qb.orderBy(new OrderBy(Statuses.STATUS_ID, false)); - qb.limit(itemLimit); - final Expression where = Expression.and(Expression.notIn(new Column(Statuses._ID), qb.build()), account_where); + qb.select(new Column(Statuses._ID)) + .from(new Tables(table)) + .where(Expression.equals(Statuses.ACCOUNT_ID, accountId)) + .orderBy(new OrderBy(Statuses.STATUS_ID, false)) + .limit(itemLimit); + final Expression where = Expression.and(Expression.lesserThan(new Column(Statuses._ID), + SQLQueryBuilder.select(SQLFunctions.MIN(new Column(Statuses._ID))).from(qb.build()).build()), accountWhere); + resolver.delete(uri, where.getSQL(), null); + } + for (final Uri uri : ACTIVITIES_URIS) { + final String table = getTableNameByUri(uri); + final Expression accountWhere = new Expression(Activities.ACCOUNT_ID + " = " + accountId); + final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); + qb.select(new Column(Activities._ID)) + .from(new Tables(table)) + .where(Expression.equals(Activities.ACCOUNT_ID, accountId)) + .orderBy(new OrderBy(Activities.TIMESTAMP, false)) + .limit(itemLimit); + final Expression where = Expression.and(Expression.lesserThan(new Column(Activities._ID), + SQLQueryBuilder.select(SQLFunctions.MIN(new Column(Activities._ID))).from(qb.build()).build()), accountWhere); resolver.delete(uri, where.getSQL(), null); } for (final Uri uri : DIRECT_MESSAGES_URIS) { final String table = getTableNameByUri(uri); - final Expression account_where = new Expression(DirectMessages.ACCOUNT_ID + " = " + accountId); + final Expression accountWhere = new Expression(DirectMessages.ACCOUNT_ID + " = " + accountId); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); - qb.select(new Column(DirectMessages._ID)).from(new Tables(table)); - qb.where(Expression.equals(DirectMessages.ACCOUNT_ID, accountId)); - qb.orderBy(new OrderBy(DirectMessages.MESSAGE_ID, false)); - qb.limit(itemLimit * 10); - final Expression where = Expression.and(Expression.notIn(new Column(DirectMessages._ID), qb.build()), account_where); + qb.select(new Column(DirectMessages._ID)) + .from(new Tables(table)) + .where(Expression.equals(DirectMessages.ACCOUNT_ID, accountId)) + .orderBy(new OrderBy(DirectMessages.MESSAGE_ID, false)) + .limit(itemLimit * 10); + final Expression where = Expression.and(Expression.lesserThan(new Column(DirectMessages._ID), + SQLQueryBuilder.select(SQLFunctions.MIN(new Column(DirectMessages._ID))).from(qb.build()).build()), accountWhere); resolver.delete(uri, where.getSQL(), null); } } @@ -674,11 +736,12 @@ public class DataStoreUtils implements Constants { final String table = getTableNameByUri(uri); if (table == null) continue; final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); - qb.select(new Column(BaseColumns._ID)); - qb.from(new Tables(table)); - qb.orderBy(new OrderBy(BaseColumns._ID, false)); - qb.limit(itemLimit * 20); - final Expression where = Expression.notIn(new Column(BaseColumns._ID), qb.build()); + qb.select(new Column(BaseColumns._ID)) + .from(new Tables(table)) + .orderBy(new OrderBy(BaseColumns._ID, false)) + .limit(itemLimit * 20); + final Expression where = Expression.lesserThan(new Column(BaseColumns._ID), + SQLQueryBuilder.select(SQLFunctions.MIN(new Column(BaseColumns._ID))).from(qb.build()).build()); resolver.delete(uri, where.getSQL(), null); } }