improved activity gap

This commit is contained in:
Mariotaku Lee 2016-04-03 20:30:31 +08:00
parent f153aea3ad
commit 1d42bca508
8 changed files with 99 additions and 38 deletions

View File

@ -8,7 +8,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
classpath 'com.android.tools.build:gradle:2.0.0-beta7' classpath 'com.android.tools.build:gradle:2.0.0-rc1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath('fr.avianey.androidsvgdrawable:gradle-plugin:3.0.0') { classpath('fr.avianey.androidsvgdrawable:gradle-plugin:3.0.0') {
// should be excluded to avoid conflict // should be excluded to avoid conflict

View File

@ -119,7 +119,7 @@ dependencies {
compile 'com.android.support:preference-v14:23.2.1' compile 'com.android.support:preference-v14:23.2.1'
compile 'com.twitter:twitter-text:1.13.0' compile 'com.twitter:twitter-text:1.13.0'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.4.1' compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.5.0'
compile 'com.squareup:otto:1.3.8' compile 'com.squareup:otto:1.3.8'
compile 'dnsjava:dnsjava:2.1.7' compile 'dnsjava:dnsjava:2.1.7'
compile 'com.commonsware.cwac:merge:1.1.1' compile 'com.commonsware.cwac:merge:1.1.1'
@ -183,4 +183,4 @@ task svgToMipmap(type: SvgDrawableTask) {
outputFormat = 'PNG' outputFormat = 'PNG'
outputType = 'mipmap' outputType = 'mipmap'
} }

View File

@ -511,8 +511,8 @@ public abstract class AbsActivitiesFragment extends AbsContentListRecyclerViewFr
final LinearLayoutManager layoutManager = getLayoutManager(); final LinearLayoutManager layoutManager = getLayoutManager();
adapter.setListener(this); adapter.setListener(this);
registerForContextMenu(recyclerView); registerForContextMenu(recyclerView);
mNavigationHelper = new RecyclerViewNavigationHelper(recyclerView, layoutManager, mNavigationHelper = new RecyclerViewNavigationHelper(recyclerView, layoutManager, adapter,
adapter, this); this);
mPauseOnScrollListener = new PauseRecyclerViewOnScrollListener(adapter.getMediaLoader().getImageLoader(), false, true); mPauseOnScrollListener = new PauseRecyclerViewOnScrollListener(adapter.getMediaLoader().getImageLoader(), false, true);
final Bundle loaderArgs = new Bundle(getArguments()); final Bundle loaderArgs = new Bundle(getArguments());
@ -521,6 +521,27 @@ public abstract class AbsActivitiesFragment extends AbsContentListRecyclerViewFr
showProgress(); showProgress();
} }
@Override
public boolean isReachingEnd() {
final LinearLayoutManager lm = getLayoutManager();
final ParcelableActivitiesAdapter adapter = getAdapter();
int lastPosition = lm.findLastCompletelyVisibleItemPosition();
final int itemCount = adapter.getItemCount();
int finalPos = itemCount - 1;
for (int i = lastPosition + 1; i < itemCount; i++) {
if (adapter.getItemViewType(i) != ParcelableActivitiesAdapter.ITEM_VIEW_TYPE_EMPTY) {
finalPos = i - 1;
break;
}
}
return finalPos >= itemCount - 1;
}
@Override
public boolean isReachingStart() {
return super.isReachingStart();
}
protected Object createMessageBusCallback() { protected Object createMessageBusCallback() {
return new StatusesBusCallback(); return new StatusesBusCallback();
} }
@ -578,11 +599,7 @@ public abstract class AbsActivitiesFragment extends AbsContentListRecyclerViewFr
} }
@Override @Override
protected void setupRecyclerView(Context context, boolean compact) { protected void setupRecyclerView(Context context, final boolean compact) {
if (compact) {
super.setupRecyclerView(context, true);
return;
}
final RecyclerView recyclerView = getRecyclerView(); final RecyclerView recyclerView = getRecyclerView();
final ParcelableActivitiesAdapter adapter = getAdapter(); final ParcelableActivitiesAdapter adapter = getAdapter();
// Dividers are drawn on bottom of view // Dividers are drawn on bottom of view
@ -595,7 +612,9 @@ public abstract class AbsActivitiesFragment extends AbsContentListRecyclerViewFr
return false; return false;
} }
final int itemViewType = adapter.getItemViewType(childPos); final int itemViewType = adapter.getItemViewType(childPos);
// Draw only if current item and next item is TITLE_SUMMARY if (compact) {
return itemViewType != ParcelableActivitiesAdapter.ITEM_VIEW_TYPE_EMPTY;
}
if (shouldUseDividerFor(itemViewType)) { if (shouldUseDividerFor(itemViewType)) {
if (shouldUseDividerFor(adapter.getItemViewType(childPos + 1))) { if (shouldUseDividerFor(adapter.getItemViewType(childPos + 1))) {
return true; return true;

View File

@ -43,7 +43,6 @@ import org.mariotaku.twidere.activity.HomeActivity;
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter; import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter;
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition; import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
import org.mariotaku.twidere.loader.ExtendedObjectCursorLoader; import org.mariotaku.twidere.loader.ExtendedObjectCursorLoader;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableActivity; import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.model.ParcelableActivityCursorIndices; import org.mariotaku.twidere.model.ParcelableActivityCursorIndices;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
@ -55,7 +54,6 @@ import org.mariotaku.twidere.model.message.GetActivitiesTaskEvent;
import org.mariotaku.twidere.model.message.StatusDestroyedEvent; import org.mariotaku.twidere.model.message.StatusDestroyedEvent;
import org.mariotaku.twidere.model.message.StatusListChangedEvent; import org.mariotaku.twidere.model.message.StatusListChangedEvent;
import org.mariotaku.twidere.model.message.StatusRetweetedEvent; import org.mariotaku.twidere.model.message.StatusRetweetedEvent;
import org.mariotaku.twidere.model.util.ParcelableAccountUtils;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.provider.TwidereDataStore.Activities; import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
import org.mariotaku.twidere.provider.TwidereDataStore.Filters; import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
@ -123,7 +121,7 @@ public abstract class CursorActivitiesFragment extends AbsActivitiesFragment {
adapter.setShowAccountsColor(accountKeys.length > 1); adapter.setShowAccountsColor(accountKeys.length > 1);
final String[] projection = Activities.COLUMNS; final String[] projection = Activities.COLUMNS;
return new CursorActivitiesLoader(context, uri, projection, selection, expression.whereArgs, return new CursorActivitiesLoader(context, uri, projection, selection, expression.whereArgs,
sortOrder, fromUser, accountKeys); sortOrder, fromUser);
} }
@Override @Override
@ -393,19 +391,17 @@ public abstract class CursorActivitiesFragment extends AbsActivitiesFragment {
} }
public static class CursorActivitiesLoader extends ExtendedObjectCursorLoader<ParcelableActivity> { public static class CursorActivitiesLoader extends ExtendedObjectCursorLoader<ParcelableActivity> {
private final UserKey[] mAccountKeys;
public CursorActivitiesLoader(Context context, Uri uri, String[] projection, public CursorActivitiesLoader(Context context, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder, String selection, String[] selectionArgs,
boolean fromUser, UserKey[] accountKeys) { String sortOrder, boolean fromUser) {
super(context, ParcelableActivityCursorIndices.class, uri, projection, selection, selectionArgs, sortOrder, fromUser); super(context, ParcelableActivityCursorIndices.class, uri, projection, selection,
mAccountKeys = accountKeys; selectionArgs, sortOrder, fromUser);
} }
@Override @Override
protected ObjectCursor<ParcelableActivity> createObjectCursor(Cursor cursor, ObjectCursor.CursorIndices<ParcelableActivity> indices) { protected ObjectCursor<ParcelableActivity> createObjectCursor(Cursor cursor, ObjectCursor.CursorIndices<ParcelableActivity> indices) {
final String[] filteredUserIds = DataStoreUtils.getFilteredUserIds(getContext()); final String[] filteredUserIds = DataStoreUtils.getFilteredUserIds(getContext());
final ParcelableAccount[] accounts = ParcelableAccountUtils.getAccounts(getContext(), mAccountKeys);
return new ActivityCursor(cursor, indices, filteredUserIds); return new ActivityCursor(cursor, indices, filteredUserIds);
} }

View File

@ -71,6 +71,7 @@ public abstract class GetActivitiesTask extends AbstractTask<RefreshTaskParam, O
if (param.shouldAbort()) return null; if (param.shouldAbort()) return null;
final UserKey[] accountIds = param.getAccountKeys(); final UserKey[] accountIds = param.getAccountKeys();
final String[] maxIds = param.getMaxIds(); final String[] maxIds = param.getMaxIds();
final long[] maxSortIds = param.getMaxSortIds();
final String[] sinceIds = param.getSinceIds(); final String[] sinceIds = param.getSinceIds();
final ContentResolver cr = context.getContentResolver(); final ContentResolver cr = context.getContentResolver();
final int loadItemLimit = preferences.getInt(KEY_LOAD_ITEM_LIMIT); final int loadItemLimit = preferences.getInt(KEY_LOAD_ITEM_LIMIT);
@ -88,8 +89,12 @@ public abstract class GetActivitiesTask extends AbstractTask<RefreshTaskParam, O
final Paging paging = new Paging(); final Paging paging = new Paging();
paging.count(loadItemLimit); paging.count(loadItemLimit);
String maxId = null; String maxId = null;
long maxSortId = -1;
if (maxIds != null) { if (maxIds != null) {
maxId = maxIds[i]; maxId = maxIds[i];
if (maxSortIds != null) {
maxSortId = maxSortIds[i];
}
if (maxId != null) { if (maxId != null) {
paging.maxId(maxId); paging.maxId(maxId);
} }
@ -139,23 +144,44 @@ public abstract class GetActivitiesTask extends AbstractTask<RefreshTaskParam, O
long[] deleteBound = new long[2]; long[] deleteBound = new long[2];
Arrays.fill(deleteBound, -1); Arrays.fill(deleteBound, -1);
List<ContentValues> valuesList = new ArrayList<>(); List<ContentValues> valuesList = new ArrayList<>();
for (Activity activity : activities) { int minIdx = -1;
final ParcelableActivity parcelableActivity = ParcelableActivityUtils.fromActivity(activity, long minPositionKey = -1;
credentials.account_key, false); if (!activities.isEmpty()) {
if (deleteBound[0] < 0) { final long firstSortId = activities.get(0).getCreatedAt().getTime();
deleteBound[0] = parcelableActivity.min_sort_position; final long lastSortId = activities.get(activities.size() - 1).getCreatedAt().getTime();
} else { // Get id diff of first and last item
deleteBound[0] = Math.min(deleteBound[0], parcelableActivity.min_sort_position); final long sortDiff = firstSortId - lastSortId;
for (int i = 0, j = activities.size(); i < j; i++) {
Activity item = activities.get(i);
final ParcelableActivity activity = ParcelableActivityUtils.fromActivity(item,
credentials.account_key, false);
activity.position_key = GetStatusesTask.getPositionKey(activity.timestamp,
activity.timestamp, lastSortId, sortDiff, i, j);
if (deleteBound[0] < 0) {
deleteBound[0] = activity.min_sort_position;
} else {
deleteBound[0] = Math.min(deleteBound[0], activity.min_sort_position);
}
if (deleteBound[1] < 0) {
deleteBound[1] = activity.max_sort_position;
} else {
deleteBound[1] = Math.max(deleteBound[1], activity.max_sort_position);
}
if (minIdx == -1 || item.compareTo(activities.get(minIdx)) < 0) {
minIdx = i;
minPositionKey = activity.position_key;
}
activity.inserted_date = System.currentTimeMillis();
final ContentValues values = ContentValuesCreator.createActivity(activity,
credentials, userColorNameManager);
valuesList.add(values);
} }
if (deleteBound[1] < 0) { }
deleteBound[1] = parcelableActivity.max_sort_position; int olderCount = -1;
} else { if (minPositionKey > 0) {
deleteBound[1] = Math.max(deleteBound[1], parcelableActivity.max_sort_position); olderCount = DataStoreUtils.getActivitiesCount(context, getContentUri(), minPositionKey,
} Activities.POSITION_KEY, false, credentials.account_key);
parcelableActivity.inserted_date = System.currentTimeMillis();
final ContentValues values = ContentValuesCreator.createActivity(parcelableActivity,
credentials, userColorNameManager);
valuesList.add(values);
} }
final Uri writeUri = UriUtils.appendQueryParameters(getContentUri(), QUERY_PARAM_NOTIFY, final Uri writeUri = UriUtils.appendQueryParameters(getContentUri(), QUERY_PARAM_NOTIFY,
notify); notify);
@ -169,7 +195,7 @@ public abstract class GetActivitiesTask extends AbstractTask<RefreshTaskParam, O
String.valueOf(deleteBound[1])}; String.valueOf(deleteBound[1])};
int rowsDeleted = cr.delete(writeUri, where.getSQL(), whereArgs); int rowsDeleted = cr.delete(writeUri, where.getSQL(), whereArgs);
// Why loadItemLimit / 2? because it will not acting strange in most cases // Why loadItemLimit / 2? because it will not acting strange in most cases
boolean insertGap = valuesList.size() >= loadItemLimit && !noItemsBefore boolean insertGap = valuesList.size() >= loadItemLimit && !noItemsBefore && olderCount > 0
&& rowsDeleted <= 0 && activities.size() > loadItemLimit / 2; && rowsDeleted <= 0 && activities.size() > loadItemLimit / 2;
if (insertGap && !valuesList.isEmpty()) { if (insertGap && !valuesList.isEmpty()) {
valuesList.get(valuesList.size() - 1).put(Activities.IS_GAP, true); valuesList.get(valuesList.size() - 1).put(Activities.IS_GAP, true);

View File

@ -193,7 +193,7 @@ public abstract class GetStatusesTask extends AbstractTask<RefreshTaskParam,
final long firstSortId = statuses.get(0).getSortId(); final long firstSortId = statuses.get(0).getSortId();
final long lastSortId = statuses.get(statuses.size() - 1).getSortId(); final long lastSortId = statuses.get(statuses.size() - 1).getSortId();
// Get id diff of first and last item // Get id diff of first and last item
long sortDiff = firstSortId - lastSortId; final long sortDiff = firstSortId - lastSortId;
for (int i = 0, j = statuses.size(); i < j; i++) { for (int i = 0, j = statuses.size(); i < j; i++) {
final Status item = statuses.get(i); final Status item = statuses.get(i);

View File

@ -486,6 +486,25 @@ public class DataStoreUtils implements Constants {
return queryCount(context, uri, selection.getSQL(), whereArgs); return queryCount(context, uri, selection.getSQL(), whereArgs);
} }
public static int getActivitiesCount(final Context context, final Uri uri, final long compare,
String compareColumn, boolean greaterThan, UserKey... accountKeys) {
if (context == null) return 0;
if (accountKeys == null) {
accountKeys = getActivatedAccountKeys(context);
}
final Expression selection = Expression.and(
Expression.inArgs(new Column(Activities.ACCOUNT_KEY), accountKeys.length),
greaterThan ? Expression.greaterThanArgs(compareColumn) : Expression.lesserThanArgs(compareColumn),
buildActivityFilterWhereClause(getTableNameByUri(uri), null)
);
final String[] whereArgs = new String[accountKeys.length + 1];
for (int i = 0; i < accountKeys.length; i++) {
whereArgs[i] = accountKeys[i].toString();
}
whereArgs[accountKeys.length] = String.valueOf(compare);
return queryCount(context, uri, selection.getSQL(), whereArgs);
}
public static int getActivitiesCount(@NonNull final Context context, final Uri uri, public static int getActivitiesCount(@NonNull final Context context, final Uri uri,
final Expression extraWhere, final String[] extraWhereArgs, final Expression extraWhere, final String[] extraWhereArgs,
final long since, String sinceColumn, boolean followingOnly, final long since, String sinceColumn, boolean followingOnly,

View File

@ -741,6 +741,7 @@ public final class Utils implements Constants {
break; break;
} }
case LINK_ID_ITEMS: { case LINK_ID_ITEMS: {
isAccountIdRequired = false;
fragment = new ItemsListFragment(); fragment = new ItemsListFragment();
break; break;
} }