mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-17 04:00:48 +01:00
improved statuses fragment load more
This commit is contained in:
parent
adaf9402a5
commit
7a85746eeb
@ -43,9 +43,9 @@ dependencies {
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'com.bluelinelabs:logansquare:1.3.4'
|
||||
compile 'org.apache.commons:commons-lang3:3.4'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.14'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.16'
|
||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
|
||||
compile 'com.github.mariotaku.SQLiteQB:library:0.9.3'
|
||||
compile 'com.github.mariotaku.SQLiteQB:library:0.9.3-SNAPSHOT'
|
||||
compile 'com.github.mariotaku.ObjectCursor:core:0.9.3'
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
|
@ -208,6 +208,8 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
|
||||
|
||||
int VIRTUAL_TABLE_ID_EMPTY = 200;
|
||||
|
||||
int VIRTUAL_TABLE_ID_RAW_QUERY = 300;
|
||||
|
||||
int NOTIFICATION_ID_HOME_TIMELINE = 1;
|
||||
int NOTIFICATION_ID_INTERACTIONS_TIMELINE = 2;
|
||||
int NOTIFICATION_ID_DIRECT_MESSAGES = 3;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package org.mariotaku.twidere.api.twitter.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.util.SimpleArrayMap;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
@ -66,15 +67,16 @@ public class TwitterConverterFactory extends RestConverter.SimpleFactory<Twitter
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T parseOrThrow(HttpResponse resp, InputStream stream, Type type)
|
||||
@NonNull
|
||||
private static <T> T parseOrThrow(InputStream stream, Type type)
|
||||
throws IOException, TwitterException, RestConverter.ConvertException {
|
||||
try {
|
||||
final ParameterizedType<T> parameterizedType = ParameterizedTypeAccessor.create(type);
|
||||
final T parse = LoganSquare.parse(stream, parameterizedType);
|
||||
if (TwitterException.class == type && parse == null) {
|
||||
throw new TwitterException();
|
||||
final T parsed = LoganSquare.parse(stream, parameterizedType);
|
||||
if (parsed == null) {
|
||||
throw new TwitterException("Empty data");
|
||||
}
|
||||
return parse;
|
||||
return parsed;
|
||||
} catch (JsonParseException e) {
|
||||
throw new RestConverter.ConvertException("Malformed JSON Data");
|
||||
}
|
||||
@ -121,7 +123,7 @@ public class TwitterConverterFactory extends RestConverter.SimpleFactory<Twitter
|
||||
public Object convert(HttpResponse httpResponse) throws IOException, ConvertException, TwitterException {
|
||||
final Body body = httpResponse.getBody();
|
||||
final InputStream stream = body.stream();
|
||||
final Object object = parseOrThrow(httpResponse, stream, type);
|
||||
final Object object = parseOrThrow(stream, type);
|
||||
checkResponse(type, object, httpResponse);
|
||||
if (object instanceof TwitterResponseObject) {
|
||||
((TwitterResponseObject) object).processResponseHeader(httpResponse);
|
||||
|
@ -37,6 +37,7 @@ import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@JsonObject
|
||||
@ -307,58 +308,61 @@ public class ParcelableMedia implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("media_url", media_url)
|
||||
.append("page_url", url)
|
||||
.append("preview_url", preview_url)
|
||||
.append("start", start)
|
||||
.append("end", end)
|
||||
.append("type", type)
|
||||
.append("width", width)
|
||||
.append("height", height)
|
||||
.append("video_info", video_info)
|
||||
.append("card", card)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ParcelableMedia media = (ParcelableMedia) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(start, media.start)
|
||||
.append(end, media.end)
|
||||
.append(type, media.type)
|
||||
.append(width, media.width)
|
||||
.append(height, media.height)
|
||||
.append(media_url, media.media_url)
|
||||
.append(url, media.url)
|
||||
.append(preview_url, media.preview_url)
|
||||
.append(video_info, media.video_info)
|
||||
.append(card, media.card)
|
||||
.isEquals();
|
||||
if (start != media.start) return false;
|
||||
if (end != media.end) return false;
|
||||
if (type != media.type) return false;
|
||||
if (width != media.width) return false;
|
||||
if (height != media.height) return false;
|
||||
if (!url.equals(media.url)) return false;
|
||||
if (media_url != null ? !media_url.equals(media.media_url) : media.media_url != null)
|
||||
return false;
|
||||
if (preview_url != null ? !preview_url.equals(media.preview_url) : media.preview_url != null)
|
||||
return false;
|
||||
if (video_info != null ? !video_info.equals(media.video_info) : media.video_info != null)
|
||||
return false;
|
||||
if (card != null ? !card.equals(media.card) : media.card != null) return false;
|
||||
return !(page_url != null ? !page_url.equals(media.page_url) : media.page_url != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(media_url)
|
||||
.append(url)
|
||||
.append(preview_url)
|
||||
.append(start)
|
||||
.append(end)
|
||||
.append(type)
|
||||
.append(width)
|
||||
.append(height)
|
||||
.append(video_info)
|
||||
.append(card)
|
||||
.toHashCode();
|
||||
int result = url.hashCode();
|
||||
result = 31 * result + (media_url != null ? media_url.hashCode() : 0);
|
||||
result = 31 * result + (preview_url != null ? preview_url.hashCode() : 0);
|
||||
result = 31 * result + start;
|
||||
result = 31 * result + end;
|
||||
result = 31 * result + type;
|
||||
result = 31 * result + width;
|
||||
result = 31 * result + height;
|
||||
result = 31 * result + (video_info != null ? video_info.hashCode() : 0);
|
||||
result = 31 * result + (card != null ? card.hashCode() : 0);
|
||||
result = 31 * result + (page_url != null ? page_url.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ParcelableMedia{" +
|
||||
"url='" + url + '\'' +
|
||||
", media_url='" + media_url + '\'' +
|
||||
", preview_url='" + preview_url + '\'' +
|
||||
", start=" + start +
|
||||
", end=" + end +
|
||||
", type=" + type +
|
||||
", width=" + width +
|
||||
", height=" + height +
|
||||
", video_info=" + video_info +
|
||||
", card=" + card +
|
||||
", page_url='" + page_url + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -439,34 +443,32 @@ public class ParcelableMedia implements Parcelable {
|
||||
return new VideoInfo(videoInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("variants", variants)
|
||||
.append("duration", duration)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
VideoInfo videoInfo = (VideoInfo) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(duration, videoInfo.duration)
|
||||
.append(variants, videoInfo.variants)
|
||||
.isEquals();
|
||||
if (duration != videoInfo.duration) return false;
|
||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
||||
return Arrays.equals(variants, videoInfo.variants);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(variants)
|
||||
.append(duration)
|
||||
.toHashCode();
|
||||
int result = variants != null ? Arrays.hashCode(variants) : 0;
|
||||
result = 31 * result + (int) (duration ^ (duration >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VideoInfo{" +
|
||||
"variants=" + Arrays.toString(variants) +
|
||||
", duration=" + duration +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -524,34 +526,32 @@ public class ParcelableMedia implements Parcelable {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Variant variant = (Variant) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(bitrate, variant.bitrate)
|
||||
.append(content_type, variant.content_type)
|
||||
.append(url, variant.url)
|
||||
.isEquals();
|
||||
if (bitrate != variant.bitrate) return false;
|
||||
if (content_type != null ? !content_type.equals(variant.content_type) : variant.content_type != null)
|
||||
return false;
|
||||
return !(url != null ? !url.equals(variant.url) : variant.url != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(content_type)
|
||||
.append(url)
|
||||
.append(bitrate)
|
||||
.toHashCode();
|
||||
int result = content_type != null ? content_type.hashCode() : 0;
|
||||
result = 31 * result + (url != null ? url.hashCode() : 0);
|
||||
result = 31 * result + (int) (bitrate ^ (bitrate >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("content_type", content_type)
|
||||
.append("url", url)
|
||||
.append("bitrate", bitrate)
|
||||
.toString();
|
||||
return "Variant{" +
|
||||
"content_type='" + content_type + '\'' +
|
||||
", url='" + url + '\'' +
|
||||
", bitrate=" + bitrate +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,6 +43,8 @@ public interface TwidereDataStore {
|
||||
|
||||
String CONTENT_PATH_EMPTY = "empty_content";
|
||||
|
||||
String CONTENT_PATH_RAW_QUERY = "raw_query";
|
||||
|
||||
String CONTENT_PATH_DATABASE_READY = "database_ready";
|
||||
|
||||
Uri BASE_CONTENT_URI = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
@ -52,6 +54,8 @@ public interface TwidereDataStore {
|
||||
|
||||
Uri CONTENT_URI_EMPTY = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH_EMPTY);
|
||||
|
||||
Uri CONTENT_URI_RAW_QUERY = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH_RAW_QUERY);
|
||||
|
||||
Uri CONTENT_URI_DATABASE_READY = Uri.withAppendedPath(BASE_CONTENT_URI,
|
||||
CONTENT_PATH_DATABASE_READY);
|
||||
|
||||
|
@ -45,7 +45,6 @@ public final class TwidereArrayUtils {
|
||||
public static boolean contentMatch(final long[] array1, final long[] array2) {
|
||||
if (array1 == null || array2 == null) return array1 == array2;
|
||||
if (array1.length != array2.length) return false;
|
||||
final int length = array1.length;
|
||||
for (long anArray1 : array1) {
|
||||
if (!ArrayUtils.contains(array2, anArray1)) return false;
|
||||
}
|
||||
@ -122,46 +121,6 @@ public final class TwidereArrayUtils {
|
||||
return array;
|
||||
}
|
||||
|
||||
public static void reverse(@NonNull Object[] array) {
|
||||
for (int i = 0; i < array.length / 2; i++) {
|
||||
Object temp = array[i];
|
||||
array[i] = array[array.length - i - 1];
|
||||
array[array.length - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] subArray(final int[] array, final int start, final int end) {
|
||||
final int length = end - start;
|
||||
if (length < 0) throw new IllegalArgumentException();
|
||||
final int[] result = new int[length];
|
||||
System.arraycopy(array, start, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long[] subArray(final long[] array, final int start, final int end) {
|
||||
final int length = end - start;
|
||||
if (length < 0) throw new IllegalArgumentException();
|
||||
final long[] result = new long[length];
|
||||
System.arraycopy(array, start, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Object[] subArray(final Object[] array, final int start, final int end) {
|
||||
final int length = end - start;
|
||||
if (length < 0) throw new IllegalArgumentException();
|
||||
final Object[] result = new Object[length];
|
||||
System.arraycopy(array, start, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String[] subArray(final String[] array, final int start, final int end) {
|
||||
final int length = end - start;
|
||||
if (length < 0) throw new IllegalArgumentException();
|
||||
final String[] result = new String[length];
|
||||
System.arraycopy(array, start, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String toString(final long[] array, final char token, final boolean include_space) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int length = array.length;
|
||||
@ -193,24 +152,24 @@ public final class TwidereArrayUtils {
|
||||
public static String[] toStringArray(final Object[] array) {
|
||||
if (array == null) return null;
|
||||
final int length = array.length;
|
||||
final String[] string_array = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
string_array[i] = ParseUtils.parseString(array[i]);
|
||||
}
|
||||
return string_array;
|
||||
}
|
||||
|
||||
|
||||
public static String[] toStringArray(final List<?> list) {
|
||||
if (list == null) return null;
|
||||
final int length = list.size();
|
||||
final String[] stringArray = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
stringArray[i] = ParseUtils.parseString(list.get(i));
|
||||
stringArray[i] = ParseUtils.parseString(array[i]);
|
||||
}
|
||||
return stringArray;
|
||||
}
|
||||
|
||||
public static String[] toStringArray(final long[] array) {
|
||||
if (array == null) return null;
|
||||
final int length = array.length;
|
||||
final String[] stringArray = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
stringArray[i] = ParseUtils.parseString(array[i]);
|
||||
}
|
||||
return stringArray;
|
||||
}
|
||||
|
||||
|
||||
public static String toStringForSQL(final String[] array) {
|
||||
final int size = array != null ? array.length : 0;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
@ -27,6 +27,7 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.mariotaku.twidere.util.TwidereArrayUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -47,28 +48,28 @@ public class ContentResolverUtils {
|
||||
final T[] colValues, final String extraWhere, final boolean valuesIsString) {
|
||||
if (resolver == null || uri == null || isEmpty(inColumn) || colValues == null || colValues.length == 0)
|
||||
return 0;
|
||||
final int col_values_length = colValues.length, blocks_count = col_values_length / MAX_BULK_COUNT + 1;
|
||||
int rows_deleted = 0;
|
||||
final int colValuesLength = colValues.length, blocks_count = colValuesLength / MAX_BULK_COUNT + 1;
|
||||
int rowsDeleted = 0;
|
||||
for (int i = 0; i < blocks_count; i++) {
|
||||
final int start = i * MAX_BULK_COUNT, end = Math.min(start + MAX_BULK_COUNT, col_values_length);
|
||||
final String[] block = TwidereArrayUtils.toStringArray(TwidereArrayUtils.subArray(colValues, start, end));
|
||||
final int start = i * MAX_BULK_COUNT, end = Math.min(start + MAX_BULK_COUNT, colValuesLength);
|
||||
final String[] block = TwidereArrayUtils.toStringArray(ArrayUtils.subarray(colValues, start, end));
|
||||
if (valuesIsString) {
|
||||
final StringBuilder where = new StringBuilder(inColumn + " IN(" + TwidereArrayUtils.toStringForSQL(block)
|
||||
+ ")");
|
||||
if (!isEmpty(extraWhere)) {
|
||||
where.append("AND ").append(extraWhere);
|
||||
}
|
||||
rows_deleted += resolver.delete(uri, where.toString(), block);
|
||||
rowsDeleted += resolver.delete(uri, where.toString(), block);
|
||||
} else {
|
||||
final StringBuilder where = new StringBuilder(inColumn + " IN("
|
||||
+ TwidereArrayUtils.toString(block, ',', true) + ")");
|
||||
if (!isEmpty(extraWhere)) {
|
||||
where.append("AND ").append(extraWhere);
|
||||
}
|
||||
rows_deleted += resolver.delete(uri, where.toString(), null);
|
||||
rowsDeleted += resolver.delete(uri, where.toString(), null);
|
||||
}
|
||||
}
|
||||
return rows_deleted;
|
||||
return rowsDeleted;
|
||||
}
|
||||
|
||||
public static int bulkInsert(final ContentResolver resolver, final Uri uri, final Collection<ContentValues> values) {
|
||||
|
@ -108,8 +108,8 @@ dependencies {
|
||||
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
|
||||
compile 'com.github.mariotaku:PickNCrop:0.9.2'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.15'
|
||||
compile 'com.github.mariotaku.RestFu:okhttp:0.9.15'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.16'
|
||||
compile 'com.github.mariotaku.RestFu:okhttp:0.9.16'
|
||||
compile 'com.github.mariotaku:InetAddressJni:0.9.1'
|
||||
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
||||
compile 'com.google.dagger:dagger:2.0.2'
|
||||
|
@ -0,0 +1,25 @@
|
||||
package android.support.v7.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/4.
|
||||
*/
|
||||
public class LinearLayoutManagerAccessor {
|
||||
|
||||
public static OrientationHelper getOrientationHelper(LinearLayoutManager llm) {
|
||||
return llm.mOrientationHelper;
|
||||
}
|
||||
|
||||
public static void ensureLayoutState(LinearLayoutManager llm) {
|
||||
llm.ensureLayoutState();
|
||||
}
|
||||
|
||||
public static boolean getShouldReverseLayout(LinearLayoutManager llm) {
|
||||
return llm.mShouldReverseLayout;
|
||||
}
|
||||
|
||||
public static View findOneVisibleChild(LinearLayoutManager llm, int fromIndex, int toIndex, boolean completelyVisible, boolean acceptPartiallyVisible) {
|
||||
return llm.findOneVisibleChild(fromIndex, toIndex, completelyVisible, acceptPartiallyVisible);
|
||||
}
|
||||
}
|
@ -647,7 +647,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
|
||||
mAccountsAdapter.setAccounts(accounts);
|
||||
|
||||
|
||||
mMediaPreviewAdapter = new MediaPreviewAdapter(this, new PreviewGridOnStartDragListener(mItemTouchHelper));
|
||||
mMediaPreviewAdapter = new MediaPreviewAdapter(this, new PreviewGridOnStartDragListener(this));
|
||||
mItemTouchHelper = new ItemTouchHelper(new AttachedMediaItemTouchHelperCallback(mMediaPreviewAdapter));
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||
@ -817,7 +817,8 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyboardShortcutRepeat(@NonNull KeyboardShortcutsHandler handler, int keyCode, int repeatCount, @NonNull KeyEvent event, int metaState) {
|
||||
public boolean handleKeyboardShortcutRepeat(@NonNull KeyboardShortcutsHandler handler, int keyCode,
|
||||
int repeatCount, @NonNull KeyEvent event, int metaState) {
|
||||
return super.handleKeyboardShortcutRepeat(handler, keyCode, repeatCount, event, metaState);
|
||||
}
|
||||
|
||||
@ -1102,7 +1103,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
|
||||
private void setMenu() {
|
||||
if (mMenuBar == null) return;
|
||||
final Menu menu = mMenuBar.getMenu();
|
||||
final boolean hasMedia = hasMedia(), hasInReplyTo = mInReplyToStatus != null;
|
||||
final boolean hasMedia = hasMedia();
|
||||
|
||||
/*
|
||||
* No media & Not reply: [Take photo][Add image][Attach location][Drafts]
|
||||
@ -1199,7 +1200,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
|
||||
if (provider != null) {
|
||||
mLocationText.setText(R.string.getting_location);
|
||||
mLocationListener = new ComposeLocationListener(this);
|
||||
// mLocationManager.requestLocationUpdates(provider, 0, 0, mLocationListener);
|
||||
mLocationManager.requestLocationUpdates(provider, 0, 0, mLocationListener);
|
||||
final Location location = Utils.getCachedLocation(this);
|
||||
if (location != null) {
|
||||
mLocationListener.onLocationChanged(location);
|
||||
@ -1880,49 +1881,48 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
|
||||
}
|
||||
|
||||
private static class PreviewGridItemDecoration extends ItemDecoration {
|
||||
private final int mPreviewGridSpacing;
|
||||
private final int previewGridSpacing;
|
||||
|
||||
public PreviewGridItemDecoration(int previewGridSpacing) {
|
||||
mPreviewGridSpacing = previewGridSpacing;
|
||||
this.previewGridSpacing = previewGridSpacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
|
||||
outRect.left = outRect.right = mPreviewGridSpacing;
|
||||
outRect.left = outRect.right = previewGridSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PreviewGridOnStartDragListener implements SimpleItemTouchHelperCallback.OnStartDragListener {
|
||||
private final WeakReference<ItemTouchHelper> helperRef;
|
||||
@NonNull
|
||||
private final ComposeActivity activity;
|
||||
|
||||
public PreviewGridOnStartDragListener(ItemTouchHelper helper) {
|
||||
helperRef = new WeakReference<>(helper);
|
||||
public PreviewGridOnStartDragListener(@NonNull ComposeActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDrag(ViewHolder viewHolder) {
|
||||
final ItemTouchHelper helper = helperRef.get();
|
||||
final ItemTouchHelper helper = activity.mItemTouchHelper;
|
||||
if (helper == null) return;
|
||||
helper.startDrag(viewHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ComposeEnterListener implements EnterListener {
|
||||
private final WeakReference<ComposeActivity> activityRef;
|
||||
private final ComposeActivity activity;
|
||||
|
||||
public ComposeEnterListener(ComposeActivity activity) {
|
||||
activityRef = new WeakReference<>(activity);
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCallListener() {
|
||||
final ComposeActivity activity = activityRef.get();
|
||||
return activity != null && activity.mKeyMetaState == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHitEnter() {
|
||||
final ComposeActivity activity = activityRef.get();
|
||||
if (activity == null) return false;
|
||||
activity.confirmAndUpdateStatus();
|
||||
return true;
|
||||
|
@ -114,7 +114,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mariotaku.twidere.util.CompareUtils.classEquals;
|
||||
import static org.mariotaku.twidere.util.Utils.cleanDatabasesByItemLimit;
|
||||
import static org.mariotaku.twidere.util.DataStoreUtils.cleanDatabasesByItemLimit;
|
||||
import static org.mariotaku.twidere.util.Utils.getDefaultAccountId;
|
||||
import static org.mariotaku.twidere.util.Utils.getTabDisplayOptionInt;
|
||||
import static org.mariotaku.twidere.util.Utils.isDatabaseReady;
|
||||
|
@ -173,7 +173,7 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
||||
ExternalBrowserPageFragment.class.getName(), args);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException(String.valueOf(media));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,12 +148,13 @@ public final class DummyStatusHolderAdapter implements IStatusesAdapter<Object>
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadMoreSupported() {
|
||||
return false;
|
||||
@IndicatorPosition
|
||||
public int getLoadMoreSupportedPosition() {
|
||||
return IndicatorPosition.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadMoreSupported(boolean supported) {
|
||||
public void setLoadMoreSupportedPosition(@IndicatorPosition int supported) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,10 @@ import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter;
|
||||
public abstract class LoadMoreSupportAdapter<VH extends ViewHolder> extends BaseRecyclerViewAdapter<VH>
|
||||
implements ILoadMoreSupportAdapter {
|
||||
|
||||
private boolean mLoadMoreSupported;
|
||||
private
|
||||
@IndicatorPosition
|
||||
int mLoadMoreIndicatorPosition;
|
||||
private int mLoadMoreSupportedPosition;
|
||||
@IndicatorPosition
|
||||
private int mLoadMoreIndicatorPosition;
|
||||
|
||||
public LoadMoreSupportAdapter(Context context) {
|
||||
super(context);
|
||||
@ -48,21 +48,20 @@ public abstract class LoadMoreSupportAdapter<VH extends ViewHolder> extends Base
|
||||
@Override
|
||||
public final void setLoadMoreIndicatorPosition(@IndicatorPosition int position) {
|
||||
if (mLoadMoreIndicatorPosition == position) return;
|
||||
mLoadMoreIndicatorPosition = mLoadMoreSupported ? position : IndicatorPosition.NONE;
|
||||
mLoadMoreIndicatorPosition = IndicatorPositionUtils.apply(position, mLoadMoreSupportedPosition);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isLoadMoreSupported() {
|
||||
return mLoadMoreSupported;
|
||||
@IndicatorPosition
|
||||
public final int getLoadMoreSupportedPosition() {
|
||||
return mLoadMoreSupportedPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLoadMoreSupported(boolean supported) {
|
||||
mLoadMoreSupported = supported;
|
||||
if (!supported) {
|
||||
mLoadMoreIndicatorPosition = IndicatorPosition.NONE;
|
||||
}
|
||||
public final void setLoadMoreSupportedPosition(@IndicatorPosition int supportedPosition) {
|
||||
mLoadMoreSupportedPosition = supportedPosition;
|
||||
mLoadMoreIndicatorPosition = IndicatorPositionUtils.apply(mLoadMoreIndicatorPosition, supportedPosition);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
@ -32,9 +32,10 @@ public interface ILoadMoreSupportAdapter {
|
||||
|
||||
void setLoadMoreIndicatorPosition(@IndicatorPosition int position);
|
||||
|
||||
boolean isLoadMoreSupported();
|
||||
@IndicatorPosition
|
||||
int getLoadMoreSupportedPosition();
|
||||
|
||||
void setLoadMoreSupported(boolean supported);
|
||||
void setLoadMoreSupportedPosition(@IndicatorPosition int supported);
|
||||
|
||||
@IntDef(flag = true, value = {IndicatorPosition.NONE, IndicatorPosition.START,
|
||||
IndicatorPosition.END, IndicatorPosition.BOTH})
|
||||
@ -44,4 +45,16 @@ public interface ILoadMoreSupportAdapter {
|
||||
int END = 0b10;
|
||||
int BOTH = START | END;
|
||||
}
|
||||
|
||||
class IndicatorPositionUtils {
|
||||
@IndicatorPosition
|
||||
public static int apply(@IndicatorPosition int orig, @IndicatorPosition int supported) {
|
||||
return orig & supported;
|
||||
}
|
||||
|
||||
@IndicatorPosition
|
||||
public static boolean has(@IndicatorPosition int flags, @IndicatorPosition int compare) {
|
||||
return (flags & compare) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import com.squareup.otto.Subscribe;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.adapter.AbsActivitiesAdapter;
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag;
|
||||
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
|
||||
import org.mariotaku.twidere.model.ParcelableActivity;
|
||||
@ -271,7 +272,7 @@ public abstract class AbsActivitiesFragment<Data> extends AbsContentListRecycler
|
||||
adapter.setData(data);
|
||||
setRefreshEnabled(true);
|
||||
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
|
||||
adapter.setLoadMoreSupported(hasMoreData(data));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(data) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
int pos = -1;
|
||||
for (int i = 0, j = adapter.getItemCount(); i < j; i++) {
|
||||
if (lastReadId != -1 && lastReadId == adapter.getTimestamp(i)) {
|
||||
|
@ -249,6 +249,7 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
|
||||
mScrollListener = new ContentListScrollListener(this);
|
||||
mRecyclerView.setOnTouchListener(mScrollListener.getOnTouchListener());
|
||||
mScrollListener.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ import com.squareup.otto.Subscribe;
|
||||
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
|
||||
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter.StatusAdapterListener;
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag;
|
||||
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
|
||||
@ -282,7 +283,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListRecyclerVi
|
||||
adapter.setData(data);
|
||||
setRefreshEnabled(true);
|
||||
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
|
||||
adapter.setLoadMoreSupported(hasMoreData(data));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(data) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
int pos = -1;
|
||||
for (int i = 0, j = adapter.getItemCount(); i < j; i++) {
|
||||
if (lastReadId != -1 && lastReadId == adapter.getStatusId(i)) {
|
||||
|
@ -23,7 +23,6 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
@ -31,6 +30,7 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.mariotaku.twidere.adapter.AbsUserListsAdapter;
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
|
||||
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
|
||||
import org.mariotaku.twidere.loader.support.iface.ICursorSupportLoader;
|
||||
import org.mariotaku.twidere.model.ParcelableUserList;
|
||||
@ -94,7 +94,7 @@ abstract class AbsUserListsFragment<Data> extends AbsContentListRecyclerViewFrag
|
||||
final AbsUserListsAdapter<Data> adapter = getAdapter();
|
||||
adapter.setData(data);
|
||||
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
|
||||
adapter.setLoadMoreSupported(hasMoreData(data));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(data) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
setRefreshEnabled(true);
|
||||
}
|
||||
if (loader instanceof IExtendedLoader) {
|
||||
|
@ -34,6 +34,7 @@ import android.view.View;
|
||||
|
||||
import org.mariotaku.twidere.adapter.AbsUsersAdapter;
|
||||
import org.mariotaku.twidere.adapter.AbsUsersAdapter.UserAdapterListener;
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
|
||||
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
|
||||
import org.mariotaku.twidere.model.ParcelableUser;
|
||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||
@ -93,7 +94,7 @@ abstract class AbsUsersFragment<Data> extends AbsContentListRecyclerViewFragment
|
||||
final AbsUsersAdapter<Data> adapter = getAdapter();
|
||||
adapter.setData(data);
|
||||
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
|
||||
adapter.setLoadMoreSupported(hasMoreData(data));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(data) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
setRefreshEnabled(true);
|
||||
}
|
||||
if (loader instanceof IExtendedLoader) {
|
||||
|
@ -226,7 +226,7 @@ public abstract class CursorActivitiesFragment extends AbsActivitiesFragment<Lis
|
||||
}
|
||||
|
||||
protected long[] getNewestActivityIds(long[] accountIds) {
|
||||
return DataStoreUtils.getActivityMaxPositionsFromDatabase(getActivity(), getContentUri(), accountIds);
|
||||
return DataStoreUtils.getNewestActivityMaxPositions(getActivity(), getContentUri(), accountIds);
|
||||
}
|
||||
|
||||
protected abstract int getNotificationType();
|
||||
|
@ -263,7 +263,7 @@ public abstract class CursorStatusesFragment extends AbsStatusesFragment<List<Pa
|
||||
}
|
||||
|
||||
protected long[] getNewestStatusIds(long[] accountIds) {
|
||||
return DataStoreUtils.getNewestStatusIdsFromDatabase(getActivity(), getContentUri(), accountIds);
|
||||
return DataStoreUtils.getNewestStatusIds(getActivity(), getContentUri(), accountIds);
|
||||
}
|
||||
|
||||
protected abstract int getNotificationType();
|
||||
|
@ -160,8 +160,7 @@ public class DirectMessagesFragment extends AbsContentListRecyclerViewFragment<M
|
||||
final MessageEntriesAdapter adapter = getAdapter();
|
||||
adapter.setCursor(cursor);
|
||||
adapter.setLoadMoreIndicatorPosition(IndicatorPosition.NONE);
|
||||
adapter.setLoadMoreSupported(!isEmpty);
|
||||
adapter.setLoadMoreSupported(hasMoreData(cursor));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(cursor) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
final long[] accountIds = getAccountIds();
|
||||
adapter.setShowAccountsColor(accountIds.length > 1);
|
||||
setRefreshEnabled(true);
|
||||
@ -233,8 +232,9 @@ public class DirectMessagesFragment extends AbsContentListRecyclerViewFragment<M
|
||||
@Override
|
||||
protected long[][] doInBackground(final Object... params) {
|
||||
final long[][] result = new long[2][];
|
||||
result[0] = DataStoreUtils.getActivatedAccountIds(getActivity());
|
||||
result[1] = DataStoreUtils.getNewestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
|
||||
result[0] = getAccountIds();
|
||||
result[1] = DataStoreUtils.getNewestMessageIds(getActivity(),
|
||||
DirectMessages.Inbox.CONTENT_URI, result[0]);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -367,9 +367,11 @@ public class DirectMessagesFragment extends AbsContentListRecyclerViewFragment<M
|
||||
@Override
|
||||
protected long[][] doInBackground(final Object... params) {
|
||||
final long[][] result = new long[3][];
|
||||
result[0] = DataStoreUtils.getActivatedAccountIds(getActivity());
|
||||
result[1] = DataStoreUtils.getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
|
||||
result[2] = DataStoreUtils.getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Outbox.CONTENT_URI);
|
||||
result[0] = getAccountIds();
|
||||
result[1] = DataStoreUtils.getOldestMessageIds(getActivity(),
|
||||
DirectMessages.Inbox.CONTENT_URI, result[0]);
|
||||
result[2] = DataStoreUtils.getOldestMessageIds(getActivity(),
|
||||
DirectMessages.Outbox.CONTENT_URI, result[0]);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ public class ScheduledStatusesFragment extends AbsContentListRecyclerViewFragmen
|
||||
super(context);
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
setLoadMoreSupported(false);
|
||||
setLoadMoreSupportedPosition(IndicatorPosition.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,7 +87,7 @@ import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
|
||||
import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
|
||||
import org.mariotaku.twidere.adapter.ArrayRecyclerAdapter;
|
||||
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter;
|
||||
import org.mariotaku.twidere.adapter.LoadMoreSupportAdapter;
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
|
||||
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
|
||||
@ -218,6 +218,23 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<ParcelableStatus>> loader, List<ParcelableStatus> data) {
|
||||
mStatusAdapter.updateItemDecoration();
|
||||
ConversationLoader conversationLoader = (ConversationLoader) loader;
|
||||
int supportedPositions = 0;
|
||||
if (data != null && !data.isEmpty()) {
|
||||
if (conversationLoader.getSinceId() < data.get(data.size() - 1).id) {
|
||||
supportedPositions |= IndicatorPosition.END;
|
||||
}
|
||||
if (data.get(0).in_reply_to_status_id > 0) {
|
||||
supportedPositions |= IndicatorPosition.START;
|
||||
}
|
||||
} else {
|
||||
supportedPositions |= IndicatorPosition.END;
|
||||
final ParcelableStatus status = getStatus();
|
||||
if (status != null && status.in_reply_to_status_id > 0) {
|
||||
supportedPositions |= IndicatorPosition.START;
|
||||
}
|
||||
}
|
||||
mStatusAdapter.setLoadMoreSupportedPosition(supportedPositions);
|
||||
setConversation(data);
|
||||
final ParcelableCredentials account = mStatusAdapter.getStatusAccount();
|
||||
if (Utils.hasOfficialAPIAccess(loader.getContext(), mPreferences, account)) {
|
||||
@ -547,8 +564,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
final ParcelableStatus status = data.getData();
|
||||
final Bundle dataExtra = data.getExtras();
|
||||
final ParcelableCredentials credentials = dataExtra.getParcelable(EXTRA_ACCOUNT);
|
||||
mStatusAdapter.setLoadMoreSupported(true);
|
||||
if (mStatusAdapter.setStatus(status, credentials)) {
|
||||
mStatusAdapter.setLoadMoreSupportedPosition(IndicatorPosition.BOTH);
|
||||
mStatusAdapter.setData(null);
|
||||
loadConversation(status, -1, -1);
|
||||
loadActivity(status);
|
||||
@ -566,7 +583,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
setState(STATE_LOADED);
|
||||
} else {
|
||||
mStatusAdapter.setLoadMoreSupported(false);
|
||||
mStatusAdapter.setLoadMoreSupportedPosition(IndicatorPosition.NONE);
|
||||
//TODO show errors
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
@ -1406,7 +1423,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
}
|
||||
|
||||
private static class StatusAdapter extends BaseRecyclerViewAdapter<ViewHolder>
|
||||
private static class StatusAdapter extends LoadMoreSupportAdapter<ViewHolder>
|
||||
implements IStatusesAdapter<List<ParcelableStatus>> {
|
||||
|
||||
private static final int VIEW_TYPE_LIST_STATUS = 0;
|
||||
@ -1416,13 +1433,14 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
private static final int VIEW_TYPE_REPLY_ERROR = 4;
|
||||
private static final int VIEW_TYPE_CONVERSATION_ERROR = 5;
|
||||
private static final int VIEW_TYPE_SPACE = 6;
|
||||
private static final int ITEM_IDX_CONVERSATION_ERROR = 0;
|
||||
private static final int ITEM_IDX_CONVERSATION_LOAD_MORE = 1;
|
||||
|
||||
private static final int ITEM_IDX_CONVERSATION_LOAD_MORE = 0;
|
||||
private static final int ITEM_IDX_CONVERSATION_ERROR = 1;
|
||||
private static final int ITEM_IDX_CONVERSATION = 2;
|
||||
private static final int ITEM_IDX_STATUS = 3;
|
||||
private static final int ITEM_IDX_REPLY = 4;
|
||||
private static final int ITEM_IDX_REPLY_LOAD_MORE = 5;
|
||||
private static final int ITEM_IDX_REPLY_ERROR = 6;
|
||||
private static final int ITEM_IDX_REPLY_ERROR = 5;
|
||||
private static final int ITEM_IDX_REPLY_LOAD_MORE = 6;
|
||||
private static final int ITEM_IDX_SPACE = 7;
|
||||
private static final int ITEM_TYPES_SUM = 8;
|
||||
|
||||
@ -1461,7 +1479,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
|
||||
private List<ParcelableStatus> mData;
|
||||
private CharSequence mReplyError, mConversationError;
|
||||
private boolean mRepliesLoading, mConversationsLoading;
|
||||
private int mReplyStart;
|
||||
|
||||
public StatusAdapter(StatusFragment fragment, boolean compact) {
|
||||
@ -1546,7 +1563,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
case ITEM_IDX_REPLY: {
|
||||
if (mData == null || mReplyStart < 0) return null;
|
||||
return mData.get(position - getIndexStart(ITEM_IDX_CONVERSATION)
|
||||
- mItemCounts[ITEM_IDX_CONVERSATION] - mItemCounts[ITEM_IDX_STATUS]
|
||||
- getTypeCount(ITEM_IDX_CONVERSATION) - getTypeCount(ITEM_IDX_STATUS)
|
||||
+ mReplyStart);
|
||||
}
|
||||
case ITEM_IDX_STATUS: {
|
||||
@ -1585,7 +1602,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
|
||||
@Override
|
||||
public int getStatusesCount() {
|
||||
return mItemCounts[ITEM_IDX_CONVERSATION] + mItemCounts[ITEM_IDX_STATUS] + mItemCounts[ITEM_IDX_REPLY];
|
||||
return getTypeCount(ITEM_IDX_CONVERSATION) + getTypeCount(ITEM_IDX_STATUS)
|
||||
+ getTypeCount(ITEM_IDX_REPLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1619,8 +1637,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
if (status == null) return;
|
||||
mData = data;
|
||||
if (data == null || data.isEmpty()) {
|
||||
setCount(ITEM_IDX_CONVERSATION, 0);
|
||||
setCount(ITEM_IDX_REPLY, 0);
|
||||
setTypeCount(ITEM_IDX_CONVERSATION, 0);
|
||||
setTypeCount(ITEM_IDX_REPLY, 0);
|
||||
mReplyStart = -1;
|
||||
} else {
|
||||
int conversationCount = 0, replyCount = 0;
|
||||
@ -1637,8 +1655,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
replyCount++;
|
||||
}
|
||||
}
|
||||
setCount(ITEM_IDX_CONVERSATION, conversationCount);
|
||||
setCount(ITEM_IDX_REPLY, replyCount);
|
||||
setTypeCount(ITEM_IDX_CONVERSATION, conversationCount);
|
||||
setTypeCount(ITEM_IDX_REPLY, replyCount);
|
||||
mReplyStart = replyStart;
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
@ -1707,36 +1725,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@IndicatorPosition
|
||||
public int getLoadMoreIndicatorPosition() {
|
||||
int position = 0;
|
||||
if (mConversationsLoading) {
|
||||
position |= IndicatorPosition.START;
|
||||
}
|
||||
if (mRepliesLoading) {
|
||||
position |= IndicatorPosition.END;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadMoreIndicatorPosition(@IndicatorPosition int position) {
|
||||
setConversationsLoading((position & IndicatorPosition.START) != 0);
|
||||
setRepliesLoading((position & IndicatorPosition.END) != 0);
|
||||
updateItemDecoration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadMoreSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadMoreSupported(boolean supported) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
@ -1819,12 +1807,12 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
case VIEW_TYPE_CONVERSATION_LOAD_INDICATOR: {
|
||||
LoadIndicatorViewHolder indicatorHolder = ((LoadIndicatorViewHolder) holder);
|
||||
indicatorHolder.setLoadProgressVisible(mConversationsLoading);
|
||||
indicatorHolder.setLoadProgressVisible(isConversationsLoading());
|
||||
break;
|
||||
}
|
||||
case VIEW_TYPE_REPLIES_LOAD_INDICATOR: {
|
||||
LoadIndicatorViewHolder indicatorHolder = ((LoadIndicatorViewHolder) holder);
|
||||
indicatorHolder.setLoadProgressVisible(mRepliesLoading);
|
||||
indicatorHolder.setLoadProgressVisible(isRepliesLoading());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1888,7 +1876,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
private int getItemType(int position) {
|
||||
int typeStart = 0;
|
||||
for (int type = 0; type < ITEM_TYPES_SUM; type++) {
|
||||
int typeCount = mItemCounts[type];
|
||||
int typeCount = getTypeCount(type);
|
||||
final int typeEnd = typeStart + typeCount;
|
||||
if (position >= typeStart && position < typeEnd) return type;
|
||||
typeStart = typeEnd;
|
||||
@ -1899,7 +1887,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
private int getItemTypeStart(int position) {
|
||||
int typeStart = 0;
|
||||
for (int type = 0; type < ITEM_TYPES_SUM; type++) {
|
||||
int typeCount = mItemCounts[type];
|
||||
int typeCount = getTypeCount(type);
|
||||
final int typeEnd = typeStart + typeCount;
|
||||
if (position >= typeStart && position < typeEnd) return typeStart;
|
||||
typeStart = typeEnd;
|
||||
@ -1932,24 +1920,28 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
mRecyclerView = null;
|
||||
}
|
||||
|
||||
private void setCount(int idx, int size) {
|
||||
private void setTypeCount(int idx, int size) {
|
||||
mItemCounts[idx] = size;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getTypeCount(int idx) {
|
||||
return mItemCounts[idx];
|
||||
}
|
||||
|
||||
public void setEventListener(StatusAdapterListener listener) {
|
||||
mStatusAdapterListener = listener;
|
||||
}
|
||||
|
||||
public void setReplyError(CharSequence error) {
|
||||
mReplyError = error;
|
||||
setCount(ITEM_IDX_REPLY_ERROR, error != null ? 1 : 0);
|
||||
setTypeCount(ITEM_IDX_REPLY_ERROR, error != null ? 1 : 0);
|
||||
updateItemDecoration();
|
||||
}
|
||||
|
||||
public void setConversationError(CharSequence error) {
|
||||
mConversationError = error;
|
||||
setCount(ITEM_IDX_CONVERSATION_ERROR, error != null ? 1 : 0);
|
||||
setTypeCount(ITEM_IDX_CONVERSATION_ERROR, error != null ? 1 : 0);
|
||||
updateItemDecoration();
|
||||
}
|
||||
|
||||
@ -1967,7 +1959,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
final DividerItemDecoration decoration = mFragment.getItemDecoration();
|
||||
decoration.setDecorationStart(0);
|
||||
// Is loading replies
|
||||
if (mRepliesLoading) {
|
||||
if (isRepliesLoading()) {
|
||||
decoration.setDecorationEndOffset(2);
|
||||
} else {
|
||||
decoration.setDecorationEndOffset(1);
|
||||
@ -1976,14 +1968,20 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
|
||||
public void setRepliesLoading(boolean loading) {
|
||||
mRepliesLoading = loading;
|
||||
notifyItemChanged(getFirstPositionOfItem(ITEM_IDX_REPLY_LOAD_MORE));
|
||||
if (loading) {
|
||||
setLoadMoreIndicatorPosition(getLoadMoreIndicatorPosition() | IndicatorPosition.END);
|
||||
} else {
|
||||
setLoadMoreIndicatorPosition(getLoadMoreIndicatorPosition() & ~IndicatorPosition.END);
|
||||
}
|
||||
updateItemDecoration();
|
||||
}
|
||||
|
||||
public void setConversationsLoading(boolean loading) {
|
||||
mConversationsLoading = loading;
|
||||
notifyItemChanged(getFirstPositionOfItem(ITEM_IDX_CONVERSATION_LOAD_MORE));
|
||||
if (loading) {
|
||||
setLoadMoreIndicatorPosition(getLoadMoreIndicatorPosition() | IndicatorPosition.START);
|
||||
} else {
|
||||
setLoadMoreIndicatorPosition(getLoadMoreIndicatorPosition() & ~IndicatorPosition.START);
|
||||
}
|
||||
updateItemDecoration();
|
||||
}
|
||||
|
||||
@ -1991,7 +1989,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
int position = 0;
|
||||
for (int i = 0; i < ITEM_TYPES_SUM; i++) {
|
||||
if (itemIdx == i) return position;
|
||||
position += mItemCounts[i];
|
||||
position += getTypeCount(i);
|
||||
}
|
||||
return RecyclerView.NO_POSITION;
|
||||
}
|
||||
@ -2010,6 +2008,14 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
return mData;
|
||||
}
|
||||
|
||||
public boolean isConversationsLoading() {
|
||||
return IndicatorPositionUtils.has(getLoadMoreIndicatorPosition(), IndicatorPosition.START);
|
||||
}
|
||||
|
||||
public boolean isRepliesLoading() {
|
||||
return IndicatorPositionUtils.has(getLoadMoreIndicatorPosition(), IndicatorPosition.END);
|
||||
}
|
||||
|
||||
public static class StatusErrorItemViewHolder extends ViewHolder {
|
||||
private final TextView textView;
|
||||
|
||||
@ -2029,6 +2035,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
private static class StatusListLinearLayoutManager extends FixedLinearLayoutManager {
|
||||
|
||||
private final RecyclerView recyclerView;
|
||||
private int mSpaceHeight;
|
||||
|
||||
public StatusListLinearLayoutManager(Context context, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
@ -2054,7 +2061,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
}
|
||||
if (heightBeforeSpace != 0) {
|
||||
final int spaceHeight = recyclerView.getMeasuredHeight() - heightBeforeSpace;
|
||||
return Math.max(0, spaceHeight);
|
||||
return mSpaceHeight = Math.max(0, spaceHeight);
|
||||
}
|
||||
}
|
||||
return super.getDecoratedMeasuredHeight(child);
|
||||
@ -2067,6 +2074,65 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
super.setOrientation(orientation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollOffset(RecyclerView.State state) {
|
||||
int offset = getScrollBarStartOffset();
|
||||
final int firstVisiblePosition = findFirstVisibleItemPosition();
|
||||
final View firstVisibleView = findViewByPosition(firstVisiblePosition);
|
||||
final float decoratedTop = getDecoratedTop(firstVisibleView),
|
||||
decoratedBottom = getDecoratedBottom(firstVisibleView);
|
||||
final float heightRatio = decoratedTop / (decoratedBottom - decoratedTop);
|
||||
return Math.round((Math.max(0, firstVisiblePosition - offset) - heightRatio)
|
||||
* getAvgItemSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollExtent(RecyclerView.State state) {
|
||||
return getAvgItemSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollRange(RecyclerView.State state) {
|
||||
final int count = getScrollBarValidItemCount();
|
||||
final int avgItemSize = getAvgItemSize();
|
||||
return count * avgItemSize;
|
||||
}
|
||||
|
||||
protected int getAvgItemSize() {
|
||||
final int firstVisiblePosition = findFirstVisibleItemPosition();
|
||||
final int lastVisiblePosition = findLastVisibleItemPosition();
|
||||
final View firstVisibleView = findViewByPosition(firstVisiblePosition);
|
||||
final View lastVisibleView = findViewByPosition(lastVisiblePosition);
|
||||
return (lastVisibleView.getBottom() - firstVisibleView.getTop()) / (lastVisiblePosition - firstVisiblePosition);
|
||||
}
|
||||
|
||||
protected int getScrollBarValidItemCount() {
|
||||
final StatusAdapter adapter = (StatusAdapter) recyclerView.getAdapter();
|
||||
int count = 0;
|
||||
if (adapter.isConversationsLoading()) {
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_CONVERSATION_LOAD_MORE);
|
||||
}
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_CONVERSATION_ERROR);
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_CONVERSATION);
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_STATUS);
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_REPLY);
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_REPLY_ERROR);
|
||||
if (adapter.isRepliesLoading() && mSpaceHeight <= 0) {
|
||||
count += adapter.getTypeCount(StatusAdapter.ITEM_IDX_REPLY_LOAD_MORE);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
protected int getScrollBarStartOffset() {
|
||||
final StatusAdapter adapter = (StatusAdapter) recyclerView.getAdapter();
|
||||
int offset = 0;
|
||||
if (!adapter.isConversationsLoading()) {
|
||||
offset = adapter.getTypeCount(StatusAdapter.ITEM_IDX_CONVERSATION_LOAD_MORE);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class StatusActivitySummaryLoader extends AsyncTaskLoader<StatusActivity> {
|
||||
|
@ -1701,7 +1701,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
|
||||
|
||||
@Override
|
||||
public SingleResponse<UserRelationship> loadInBackground() {
|
||||
final boolean isFiltering = Utils.isFilteringUser(context, userId);
|
||||
final boolean isFiltering = DataStoreUtils.isFilteringUser(context, userId);
|
||||
if (accountId == userId)
|
||||
return SingleResponse.getInstance();
|
||||
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(context, accountId, false);
|
||||
|
@ -111,7 +111,7 @@ public class UserMediaTimelineFragment extends AbsContentRecyclerViewFragment<St
|
||||
final StaggeredGridParcelableStatusesAdapter adapter = getAdapter();
|
||||
adapter.setData(data);
|
||||
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
|
||||
adapter.setLoadMoreSupported(hasMoreData(data));
|
||||
adapter.setLoadMoreSupportedPosition(hasMoreData(data) ? IndicatorPosition.END : IndicatorPosition.NONE);
|
||||
}
|
||||
if (loader instanceof IExtendedLoader) {
|
||||
((IExtendedLoader) loader).setFromUser(false);
|
||||
|
@ -51,6 +51,7 @@ import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationCompat.InboxStyle;
|
||||
import android.support.v4.text.BidiFormatter;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
@ -168,6 +169,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
||||
Bus mBus;
|
||||
@Inject
|
||||
UserColorNameManager mUserColorNameManager;
|
||||
@Inject
|
||||
BidiFormatter mBidiFormatter;
|
||||
|
||||
private Handler mHandler;
|
||||
private ContentResolver mContentResolver;
|
||||
@ -745,6 +748,12 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
||||
case VIRTUAL_TABLE_ID_EMPTY: {
|
||||
return new MatrixCursor(projection);
|
||||
}
|
||||
case VIRTUAL_TABLE_ID_RAW_QUERY: {
|
||||
if (projection != null || selection != null || sortOrder != null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return mDatabaseWrapper.rawQuery(uri.getLastPathSegment(), selectionArgs);
|
||||
}
|
||||
}
|
||||
if (table == null) return null;
|
||||
final Cursor c = mDatabaseWrapper.query(table, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
@ -1163,8 +1172,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
||||
if (uri == null) return;
|
||||
switch (tableId) {
|
||||
case TABLE_ID_ACCOUNTS: {
|
||||
Utils.clearAccountColor();
|
||||
Utils.clearAccountName();
|
||||
DataStoreUtils.clearAccountColor();
|
||||
DataStoreUtils.clearAccountName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.receiver.PowerStateReceiver;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.DataStoreUtils;
|
||||
import org.mariotaku.twidere.util.DebugModeUtils;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||
|
||||
@ -88,7 +87,7 @@ public class RefreshService extends Service implements Constants {
|
||||
final AccountPreferences[] accountPrefs = AccountPreferences.getAccountPreferences(context, accountIds);
|
||||
if (BROADCAST_REFRESH_HOME_TIMELINE.equals(action)) {
|
||||
final long[] refreshIds = getRefreshableIds(accountPrefs, new HomeRefreshableFilter());
|
||||
final long[] sinceIds = DataStoreUtils.getNewestStatusIdsFromDatabase(context, Statuses.CONTENT_URI, refreshIds);
|
||||
final long[] sinceIds = DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, refreshIds);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(LOGTAG, String.format("Auto refreshing home for %s", Arrays.toString(refreshIds)));
|
||||
}
|
||||
@ -105,7 +104,7 @@ public class RefreshService extends Service implements Constants {
|
||||
}
|
||||
} else if (BROADCAST_REFRESH_DIRECT_MESSAGES.equals(action)) {
|
||||
final long[] refreshIds = getRefreshableIds(accountPrefs, new MessagesRefreshableFilter());
|
||||
final long[] sinceIds = DataStoreUtils.getNewestMessageIdsFromDatabase(context,
|
||||
final long[] sinceIds = DataStoreUtils.getNewestMessageIds(context,
|
||||
DirectMessages.Inbox.CONTENT_URI,
|
||||
refreshIds);
|
||||
if (BuildConfig.DEBUG) {
|
||||
|
@ -49,10 +49,10 @@ public abstract class GetActivitiesTask extends ManagedAsyncTask<Object, Object,
|
||||
final Context context = twitterWrapper.getContext();
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
final int loadItemLimit = twitterWrapper.getPreferences().getInt(KEY_LOAD_ITEM_LIMIT);
|
||||
boolean getReadPosition = false;
|
||||
boolean saveReadPosition = false;
|
||||
for (int i = 0; i < accountIds.length; i++) {
|
||||
final long accountId = accountIds[i];
|
||||
final boolean noItemsBefore = DataStoreUtils.getActivityCountInDatabase(context,
|
||||
final boolean noItemsBefore = DataStoreUtils.getActivitiesCount(context,
|
||||
getContentUri(), accountId) <= 0;
|
||||
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(context, accountId,
|
||||
true);
|
||||
@ -65,46 +65,16 @@ public abstract class GetActivitiesTask extends ManagedAsyncTask<Object, Object,
|
||||
paging.sinceId(sinceIds[i]);
|
||||
if (maxIds == null || maxIds[i] <= 0) {
|
||||
paging.setLatestResults(true);
|
||||
getReadPosition = true;
|
||||
saveReadPosition = true;
|
||||
}
|
||||
}
|
||||
// We should delete old activities has intersection with new items
|
||||
long[] deleteBound = new long[2];
|
||||
Arrays.fill(deleteBound, -1);
|
||||
try {
|
||||
List<ContentValues> valuesList = new ArrayList<>();
|
||||
for (Activity activity : getActivities(accountId, twitter, paging)) {
|
||||
final ParcelableActivity parcelableActivity = new ParcelableActivity(activity, accountId, false);
|
||||
if (deleteBound[0] < 0) {
|
||||
deleteBound[0] = parcelableActivity.min_position;
|
||||
} else {
|
||||
deleteBound[0] = Math.min(deleteBound[0], parcelableActivity.min_position);
|
||||
}
|
||||
if (deleteBound[1] < 0) {
|
||||
deleteBound[1] = parcelableActivity.max_position;
|
||||
} else {
|
||||
deleteBound[1] = Math.max(deleteBound[1], parcelableActivity.max_position);
|
||||
}
|
||||
final ContentValues values = ContentValuesCreator.createActivity(parcelableActivity);
|
||||
values.put(Statuses.INSERTED_DATE, System.currentTimeMillis());
|
||||
valuesList.add(values);
|
||||
}
|
||||
if (deleteBound[0] > 0 && deleteBound[1] > 0) {
|
||||
Expression where = Expression.and(
|
||||
Expression.equals(Activities.ACCOUNT_ID, accountId),
|
||||
Expression.greaterEquals(Activities.MIN_POSITION, deleteBound[0]),
|
||||
Expression.lesserEquals(Activities.MAX_POSITION, deleteBound[1])
|
||||
);
|
||||
int rowsDeleted = cr.delete(getContentUri(), where.getSQL(), null);
|
||||
boolean insertGap = valuesList.size() >= loadItemLimit && !noItemsBefore
|
||||
&& rowsDeleted <= 0;
|
||||
if (insertGap && !valuesList.isEmpty()) {
|
||||
valuesList.get(valuesList.size() - 1).put(Activities.IS_GAP, true);
|
||||
}
|
||||
}
|
||||
ContentResolverUtils.bulkInsert(cr, getContentUri(), valuesList);
|
||||
if (getReadPosition) {
|
||||
getReadPosition(accountId, twitter);
|
||||
final ResponseList<Activity> activities = getActivities(accountId, twitter, paging);
|
||||
storeActivities(cr, loadItemLimit, accountId, noItemsBefore, activities);
|
||||
// if (saveReadPosition && TwitterAPIFactory.isOfficialTwitterInstance(context, twitter)) {
|
||||
if (saveReadPosition) {
|
||||
saveReadPosition(accountId, twitter);
|
||||
}
|
||||
} catch (TwitterException e) {
|
||||
|
||||
@ -113,7 +83,44 @@ public abstract class GetActivitiesTask extends ManagedAsyncTask<Object, Object,
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract void getReadPosition(long accountId, Twitter twitter);
|
||||
private void storeActivities(ContentResolver cr, int loadItemLimit, long accountId,
|
||||
boolean noItemsBefore, ResponseList<Activity> activities) {
|
||||
long[] deleteBound = new long[2];
|
||||
Arrays.fill(deleteBound, -1);
|
||||
List<ContentValues> valuesList = new ArrayList<>();
|
||||
for (Activity activity : activities) {
|
||||
final ParcelableActivity parcelableActivity = new ParcelableActivity(activity, accountId, false);
|
||||
if (deleteBound[0] < 0) {
|
||||
deleteBound[0] = parcelableActivity.min_position;
|
||||
} else {
|
||||
deleteBound[0] = Math.min(deleteBound[0], parcelableActivity.min_position);
|
||||
}
|
||||
if (deleteBound[1] < 0) {
|
||||
deleteBound[1] = parcelableActivity.max_position;
|
||||
} else {
|
||||
deleteBound[1] = Math.max(deleteBound[1], parcelableActivity.max_position);
|
||||
}
|
||||
final ContentValues values = ContentValuesCreator.createActivity(parcelableActivity);
|
||||
values.put(Statuses.INSERTED_DATE, System.currentTimeMillis());
|
||||
valuesList.add(values);
|
||||
}
|
||||
if (deleteBound[0] > 0 && deleteBound[1] > 0) {
|
||||
Expression where = Expression.and(
|
||||
Expression.equals(Activities.ACCOUNT_ID, accountId),
|
||||
Expression.greaterEquals(Activities.MIN_POSITION, deleteBound[0]),
|
||||
Expression.lesserEquals(Activities.MAX_POSITION, deleteBound[1])
|
||||
);
|
||||
int rowsDeleted = cr.delete(getContentUri(), where.getSQL(), null);
|
||||
boolean insertGap = valuesList.size() >= loadItemLimit && !noItemsBefore
|
||||
&& rowsDeleted <= 0;
|
||||
if (insertGap && !valuesList.isEmpty()) {
|
||||
valuesList.get(valuesList.size() - 1).put(Activities.IS_GAP, true);
|
||||
}
|
||||
}
|
||||
ContentResolverUtils.bulkInsert(cr, getContentUri(), valuesList);
|
||||
}
|
||||
|
||||
protected abstract void saveReadPosition(long accountId, Twitter twitter);
|
||||
|
||||
protected abstract ResponseList<Activity> getActivities(long accountId, Twitter twitter, Paging paging) throws TwitterException;
|
||||
|
||||
|
@ -81,7 +81,7 @@ public abstract class GetStatusesTask extends ManagedAsyncTask<Object, TwitterWr
|
||||
final Uri uri = getDatabaseUri();
|
||||
final Context context = twitterWrapper.getContext();
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final boolean noItemsBefore = DataStoreUtils.getStatusCountInDatabase(context, uri, accountId) <= 0;
|
||||
final boolean noItemsBefore = DataStoreUtils.getStatusCount(context, uri, accountId) <= 0;
|
||||
final ContentValues[] values = new ContentValues[statuses.size()];
|
||||
final long[] statusIds = new long[statuses.size()];
|
||||
long minId = -1;
|
||||
|
@ -400,13 +400,13 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||
@Override
|
||||
protected Object[] doInBackground(long[][] params) {
|
||||
final Object[] result = new Object[8];
|
||||
result[0] = DataStoreUtils.getNewestStatusIdsFromDatabase(mContext, Statuses.CONTENT_URI, accountIds);
|
||||
result[0] = DataStoreUtils.getNewestStatusIds(mContext, Statuses.CONTENT_URI, accountIds);
|
||||
if (Boolean.TRUE.equals(result[1] = mPreferences.getBoolean(KEY_HOME_REFRESH_MENTIONS))) {
|
||||
result[2] = DataStoreUtils.getActivityMaxPositionsFromDatabase(mContext,
|
||||
result[2] = DataStoreUtils.getNewestActivityMaxPositions(mContext,
|
||||
Activities.AboutMe.CONTENT_URI, accountIds);
|
||||
}
|
||||
if (Boolean.TRUE.equals(result[3] = mPreferences.getBoolean(KEY_HOME_REFRESH_DIRECT_MESSAGES))) {
|
||||
result[4] = DataStoreUtils.getNewestMessageIdsFromDatabase(mContext, DirectMessages.Inbox.CONTENT_URI, accountIds);
|
||||
result[4] = DataStoreUtils.getNewestMessageIds(mContext, DirectMessages.Inbox.CONTENT_URI, accountIds);
|
||||
}
|
||||
if (Boolean.TRUE.equals(result[5] = mPreferences.getBoolean(KEY_HOME_REFRESH_TRENDS))) {
|
||||
result[6] = Utils.getDefaultAccountId(mContext);
|
||||
@ -535,7 +535,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||
mAsyncTaskManager.add(new GetActivitiesTask(this, TASK_TAG_GET_MENTIONS, accountIds, maxIds, sinceIds) {
|
||||
|
||||
@Override
|
||||
protected void getReadPosition(long accountId, Twitter twitter) {
|
||||
protected void saveReadPosition(long accountId, Twitter twitter) {
|
||||
try {
|
||||
CursorTimestampResponse response = twitter.getActivitiesAboutMeUnread(true);
|
||||
final String tag = Utils.getReadPositionTagWithAccounts(ReadPositionTag.ACTIVITIES_ABOUT_ME, accountIds);
|
||||
@ -568,7 +568,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||
mAsyncTaskManager.add(new GetActivitiesTask(this, "get_activities_by_friends", accountIds, maxIds, sinceIds) {
|
||||
|
||||
@Override
|
||||
protected void getReadPosition(long accountId, Twitter twitter) {
|
||||
protected void saveReadPosition(long accountId, Twitter twitter) {
|
||||
|
||||
}
|
||||
|
||||
@ -1078,7 +1078,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||
values.put(CachedRelationships.FOLLOWED_BY, false);
|
||||
mResolver.update(CachedRelationships.CONTENT_URI, values,
|
||||
Expression.inArgs(CachedRelationships.USER_ID, list.size()).getSQL(),
|
||||
TwidereArrayUtils.toStringArray(list));
|
||||
TwidereListUtils.toStringArray(list));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,7 +78,7 @@ public class ContentListScrollListener extends OnScrollListener {
|
||||
final Object adapter = mContentListSupport.getAdapter();
|
||||
if (!(adapter instanceof ILoadMoreSupportAdapter)) return;
|
||||
final ILoadMoreSupportAdapter loadMoreAdapter = (ILoadMoreSupportAdapter) adapter;
|
||||
if (!mContentListSupport.isRefreshing() && loadMoreAdapter.isLoadMoreSupported()
|
||||
if (!mContentListSupport.isRefreshing() && loadMoreAdapter.getLoadMoreSupportedPosition() != IndicatorPosition.NONE
|
||||
&& loadMoreAdapter.getLoadMoreIndicatorPosition() == IndicatorPosition.NONE) {
|
||||
int position = 0;
|
||||
if (mContentListSupport.isReachingEnd() && mScrollDirection >= 0) {
|
||||
@ -87,6 +87,7 @@ public class ContentListScrollListener extends OnScrollListener {
|
||||
if (mContentListSupport.isReachingStart() && mScrollDirection <= 0) {
|
||||
position |= IndicatorPosition.START;
|
||||
}
|
||||
resetScrollDirection();
|
||||
mContentListSupport.onLoadMoreContents(position);
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,17 @@ import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.mariotaku.sqliteqb.library.ArgsArray;
|
||||
import org.mariotaku.sqliteqb.library.Columns;
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column;
|
||||
import org.mariotaku.sqliteqb.library.Expression;
|
||||
import org.mariotaku.sqliteqb.library.OrderBy;
|
||||
import org.mariotaku.sqliteqb.library.RawItemArray;
|
||||
import org.mariotaku.sqliteqb.library.SQLFunctions;
|
||||
import org.mariotaku.sqliteqb.library.SQLQueryBuilder;
|
||||
@ -67,6 +72,9 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/11/28.
|
||||
@ -74,6 +82,8 @@ import static android.text.TextUtils.isEmpty;
|
||||
public class DataStoreUtils implements Constants {
|
||||
static final UriMatcher CONTENT_PROVIDER_URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
static LongSparseArray<Integer> sAccountColors = new LongSparseArray<>();
|
||||
static LongSparseArray<String> sAccountScreenNames = new LongSparseArray<>();
|
||||
static LongSparseArray<String> sAccountNames = new LongSparseArray<>();
|
||||
|
||||
static {
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Accounts.CONTENT_PATH,
|
||||
@ -167,236 +177,61 @@ public class DataStoreUtils implements Constants {
|
||||
VIRTUAL_TABLE_ID_SUGGESTIONS_SEARCH);
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, TwidereDataStore.CONTENT_PATH_EMPTY,
|
||||
VIRTUAL_TABLE_ID_EMPTY);
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, TwidereDataStore.CONTENT_PATH_RAW_QUERY,
|
||||
VIRTUAL_TABLE_ID_RAW_QUERY);
|
||||
}
|
||||
|
||||
static LongSparseArray<String> sAccountScreenNames = new LongSparseArray<>();
|
||||
static LongSparseArray<String> sAccountNames = new LongSparseArray<>();
|
||||
|
||||
public static long[] getNewestMessageIdsFromDatabase(final Context context, final Uri uri) {
|
||||
final long[] accountIds = getActivatedAccountIds(context);
|
||||
return getNewestMessageIdsFromDatabase(context, uri, accountIds);
|
||||
@NonNull
|
||||
public static long[] getNewestMessageIds(final Context context, final Uri uri, final long[] accountIds) {
|
||||
return getLongFieldArray(context, uri, accountIds, DirectMessages.ACCOUNT_ID, DirectMessages.MESSAGE_ID,
|
||||
new OrderBy(SQLFunctions.MAX(DirectMessages.MESSAGE_TIMESTAMP)));
|
||||
}
|
||||
|
||||
public static long[] getNewestMessageIdsFromDatabase(final Context context, final Uri uri, final long[] accountIds) {
|
||||
if (context == null || uri == null || accountIds == null) return null;
|
||||
final String[] cols = new String[]{DirectMessages.MESSAGE_ID};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] messageIds = new long[accountIds.length];
|
||||
int idx = 0;
|
||||
for (final long accountId : accountIds) {
|
||||
final String where = Expression.equals(DirectMessages.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, cols, where, null,
|
||||
DirectMessages.DEFAULT_SORT_ORDER);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getCount() > 0) {
|
||||
cur.moveToFirst();
|
||||
messageIds[idx] = cur.getLong(cur.getColumnIndexOrThrow(DirectMessages.MESSAGE_ID));
|
||||
}
|
||||
cur.close();
|
||||
idx++;
|
||||
}
|
||||
return messageIds;
|
||||
@NonNull
|
||||
public static long[] getNewestStatusIds(final Context context, final Uri uri, final long[] accountIds) {
|
||||
return getLongFieldArray(context, uri, accountIds, Statuses.ACCOUNT_ID, Statuses.STATUS_ID,
|
||||
new OrderBy(SQLFunctions.MAX(Statuses.STATUS_TIMESTAMP)));
|
||||
}
|
||||
|
||||
public static long[] getNewestStatusIdsFromDatabase(final Context context, final Uri uri) {
|
||||
final long[] account_ids = getActivatedAccountIds(context);
|
||||
return getNewestStatusIdsFromDatabase(context, uri, account_ids);
|
||||
}
|
||||
|
||||
public static long[] getNewestStatusIdsFromDatabase(final Context context, final Uri uri, final long[] accountIds) {
|
||||
if (context == null || uri == null || accountIds == null) return null;
|
||||
final String[] cols = new String[]{Statuses.STATUS_ID};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] status_ids = new long[accountIds.length];
|
||||
int idx = 0;
|
||||
for (final long accountId : accountIds) {
|
||||
final String where = Expression.equals(Statuses.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils
|
||||
.query(resolver, uri, cols, where, null, Statuses.DEFAULT_SORT_ORDER);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getCount() > 0) {
|
||||
cur.moveToFirst();
|
||||
status_ids[idx] = cur.getLong(cur.getColumnIndexOrThrow(Statuses.STATUS_ID));
|
||||
}
|
||||
cur.close();
|
||||
idx++;
|
||||
}
|
||||
return status_ids;
|
||||
}
|
||||
|
||||
public static long[] getActivityMaxPositionsFromDatabase(final Context context, final Uri uri, final long[] accountIds) {
|
||||
if (context == null || uri == null || accountIds == null) return null;
|
||||
final String[] cols = new String[]{Activities.MAX_POSITION};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] maxPositions = new long[accountIds.length];
|
||||
int idx = 0;
|
||||
for (final long accountId : accountIds) {
|
||||
final String where = Expression.equals(Activities.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils
|
||||
.query(resolver, uri, cols, where, null, Activities.DEFAULT_SORT_ORDER);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getCount() > 0) {
|
||||
cur.moveToFirst();
|
||||
maxPositions[idx] = cur.getLong(cur.getColumnIndexOrThrow(Activities.MAX_POSITION));
|
||||
}
|
||||
cur.close();
|
||||
idx++;
|
||||
}
|
||||
return maxPositions;
|
||||
}
|
||||
|
||||
public static long[] getNewestActivityTimestampsFromDatabase(final Context context, final Uri uri, final long[] accountIds) {
|
||||
if (context == null || uri == null || accountIds == null) return null;
|
||||
final String[] cols = new String[]{Activities.TIMESTAMP};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] maxPositions = new long[accountIds.length];
|
||||
int idx = 0;
|
||||
for (final long accountId : accountIds) {
|
||||
final String where = Expression.equals(Activities.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils
|
||||
.query(resolver, uri, cols, where, null, Activities.DEFAULT_SORT_ORDER);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getCount() > 0) {
|
||||
cur.moveToFirst();
|
||||
maxPositions[idx] = cur.getLong(cur.getColumnIndexOrThrow(Activities.TIMESTAMP));
|
||||
}
|
||||
cur.close();
|
||||
idx++;
|
||||
}
|
||||
return maxPositions;
|
||||
}
|
||||
|
||||
public static long[] getOldestMessageIdsFromDatabase(final Context context, final Uri uri) {
|
||||
final long[] account_ids = getActivatedAccountIds(context);
|
||||
return getOldestMessageIdsFromDatabase(context, uri, account_ids);
|
||||
}
|
||||
|
||||
public static long[] getOldestMessageIdsFromDatabase(final Context context, final Uri uri, final long[] accountIds) {
|
||||
if (context == null || uri == null) return null;
|
||||
final String[] cols = new String[]{DirectMessages.MESSAGE_ID};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] status_ids = new long[accountIds.length];
|
||||
int idx = 0;
|
||||
for (final long accountId : accountIds) {
|
||||
final String where = Expression.equals(DirectMessages.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, cols, where, null, DirectMessages.MESSAGE_ID);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getCount() > 0) {
|
||||
cur.moveToFirst();
|
||||
status_ids[idx] = cur.getLong(cur.getColumnIndexOrThrow(DirectMessages.MESSAGE_ID));
|
||||
}
|
||||
cur.close();
|
||||
idx++;
|
||||
}
|
||||
return status_ids;
|
||||
@NonNull
|
||||
public static long[] getOldestMessageIds(@NonNull final Context context, @NonNull final Uri uri,
|
||||
@NonNull final long[] accountIds) {
|
||||
return getLongFieldArray(context, uri, accountIds, DirectMessages.ACCOUNT_ID,
|
||||
DirectMessages.MESSAGE_ID, new OrderBy(SQLFunctions.MIN(DirectMessages.MESSAGE_TIMESTAMP)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static long[] getOldestStatusIds(@NonNull final Context context, @NonNull final Uri uri,
|
||||
@NonNull final long[] accountIds) {
|
||||
final String[] cols = new String[]{Statuses.STATUS_ID};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] statusIds = new long[accountIds.length];
|
||||
Arrays.fill(statusIds, -1);
|
||||
for (int i = 0, j = accountIds.length; i < j; i++) {
|
||||
long accountId = accountIds[i];
|
||||
final String where = Expression.equals(Statuses.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, cols, where, null, Statuses.STATUS_ID);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (cur.moveToFirst()) {
|
||||
statusIds[i] = cur.getLong(cur.getColumnIndexOrThrow(Statuses.STATUS_ID));
|
||||
}
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
return statusIds;
|
||||
return getLongFieldArray(context, uri, accountIds, Statuses.ACCOUNT_ID, Statuses.STATUS_ID,
|
||||
new OrderBy(SQLFunctions.MIN(Statuses.STATUS_TIMESTAMP)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static long[] getOldestActivityMinPositions(@NonNull final Context context,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final long[] accountIds) {
|
||||
return getOldestActivityLongField(context, uri, accountIds, Activities.MIN_POSITION);
|
||||
public static long[] getNewestActivityMaxPositions(final Context context, final Uri uri, final long[] accountIds) {
|
||||
return getLongFieldArray(context, uri, accountIds, Activities.ACCOUNT_ID,
|
||||
Activities.MAX_POSITION, new OrderBy(SQLFunctions.MAX(Activities.TIMESTAMP)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static long[] getOldestActivityMaxPositions(@NonNull final Context context,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final long[] accountIds) {
|
||||
return getOldestActivityLongField(context, uri, accountIds, Activities.MAX_POSITION);
|
||||
return getLongFieldArray(context, uri, accountIds, Activities.ACCOUNT_ID,
|
||||
Activities.MAX_POSITION, new OrderBy(SQLFunctions.MIN(Activities.TIMESTAMP)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static long[] getOldestActivityLongField(@NonNull final Context context,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final long[] accountIds,
|
||||
@NonNull final String column) {
|
||||
final String[] cols = new String[]{column};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] activityIds = new long[accountIds.length];
|
||||
for (int i = 0, j = accountIds.length; i < j; i++) {
|
||||
long accountId = accountIds[i];
|
||||
final String where = Expression.equals(Activities.ACCOUNT_ID, accountId).getSQL();
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, cols, where, null, Activities.TIMESTAMP);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (cur.moveToFirst()) {
|
||||
activityIds[i] = cur.getLong(cur.getColumnIndexOrThrow(column));
|
||||
}
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
return activityIds;
|
||||
}
|
||||
|
||||
public static int getStatusCountInDatabase(final Context context, final Uri uri, final long accountId) {
|
||||
public static int getStatusCount(final Context context, final Uri uri, final long accountId) {
|
||||
final String where = Expression.equals(Statuses.ACCOUNT_ID, accountId).getSQL();
|
||||
return queryCount(context, uri, where, null);
|
||||
}
|
||||
|
||||
public static int getActivityCountInDatabase(final Context context, final Uri uri, final long accountId) {
|
||||
public static int getActivitiesCount(final Context context, final Uri uri, final long accountId) {
|
||||
final String where = Expression.equals(Activities.ACCOUNT_ID, accountId).getSQL();
|
||||
return queryCount(context, uri, where, null);
|
||||
}
|
||||
|
||||
public static int queryCount(final Context context, final Uri uri, final String selection, final String[] selectionArgs) {
|
||||
if (context == null) return -1;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] projection = new String[]{SQLFunctions.COUNT()};
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, projection, selection, selectionArgs, null);
|
||||
if (cur == null) return -1;
|
||||
try {
|
||||
if (cur.moveToFirst()) {
|
||||
return cur.getInt(0);
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static long[] getFilteredUserIds(Context context) {
|
||||
@ -424,48 +259,48 @@ public class DataStoreUtils implements Constants {
|
||||
@NonNull
|
||||
public static Expression buildStatusFilterWhereClause(@NonNull final String table, final Expression extraSelection) {
|
||||
final SQLSelectQuery filteredUsersQuery = SQLQueryBuilder
|
||||
.select(new Columns.Column(new Table(Filters.Users.TABLE_NAME), Filters.Users.USER_ID))
|
||||
.select(new Column(new Table(Filters.Users.TABLE_NAME), Filters.Users.USER_ID))
|
||||
.from(new Tables(Filters.Users.TABLE_NAME))
|
||||
.build();
|
||||
final Expression filteredUsersWhere = Expression.or(
|
||||
Expression.in(new Columns.Column(new Table(table), Statuses.USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Columns.Column(new Table(table), Statuses.RETWEETED_BY_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Columns.Column(new Table(table), Statuses.QUOTED_USER_ID), filteredUsersQuery)
|
||||
Expression.in(new Column(new Table(table), Statuses.USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Column(new Table(table), Statuses.RETWEETED_BY_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Column(new Table(table), Statuses.QUOTED_USER_ID), filteredUsersQuery)
|
||||
);
|
||||
final SQLSelectQuery.Builder filteredIdsQueryBuilder = SQLQueryBuilder
|
||||
.select(new Columns.Column(new Table(table), Statuses._ID))
|
||||
.select(new Column(new Table(table), Statuses._ID))
|
||||
.from(new Tables(table))
|
||||
.where(filteredUsersWhere)
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Statuses._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Statuses._ID)))
|
||||
.from(new Tables(table, Filters.Sources.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.SOURCE),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.QUOTED_SOURCE),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.QUOTED_SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'")
|
||||
))
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Statuses._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Statuses._ID)))
|
||||
.from(new Tables(table, Filters.Keywords.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.TEXT_PLAIN),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.QUOTED_TEXT_PLAIN),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.QUOTED_TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'")
|
||||
))
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Statuses._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Statuses._ID)))
|
||||
.from(new Tables(table, Filters.Links.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.TEXT_HTML),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.TEXT_HTML),
|
||||
"'%>%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%</a>%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Statuses.QUOTED_TEXT_HTML),
|
||||
Expression.likeRaw(new Column(new Table(table), Statuses.QUOTED_TEXT_HTML),
|
||||
"'%>%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%</a>%'")
|
||||
));
|
||||
final Expression filterExpression = Expression.or(
|
||||
Expression.notIn(new Columns.Column(new Table(table), Statuses._ID), filteredIdsQueryBuilder.build()),
|
||||
Expression.equals(new Columns.Column(new Table(table), Statuses.IS_GAP), 1)
|
||||
Expression.notIn(new Column(new Table(table), Statuses._ID), filteredIdsQueryBuilder.build()),
|
||||
Expression.equals(new Column(new Table(table), Statuses.IS_GAP), 1)
|
||||
);
|
||||
if (extraSelection != null) {
|
||||
return Expression.and(filterExpression, extraSelection);
|
||||
@ -473,28 +308,6 @@ public class DataStoreUtils implements Constants {
|
||||
return filterExpression;
|
||||
}
|
||||
|
||||
public static String[] getAccountNames(final Context context, final long[] accountIds) {
|
||||
if (context == null) return new String[0];
|
||||
final String[] cols = new String[]{Accounts.NAME};
|
||||
final String where = accountIds != null ? Expression.in(new Columns.Column(Accounts.ACCOUNT_ID),
|
||||
new RawItemArray(accountIds)).getSQL() : null;
|
||||
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where,
|
||||
null, null);
|
||||
if (cur == null) return new String[0];
|
||||
try {
|
||||
cur.moveToFirst();
|
||||
final String[] names = new String[cur.getCount()];
|
||||
int i = 0;
|
||||
while (!cur.isAfterLast()) {
|
||||
names[i++] = cur.getString(0);
|
||||
cur.moveToNext();
|
||||
}
|
||||
return names;
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccountScreenName(final Context context, final long accountId) {
|
||||
if (context == null) return null;
|
||||
final String cached = sAccountScreenNames.get(accountId);
|
||||
@ -515,22 +328,13 @@ public class DataStoreUtils implements Constants {
|
||||
}
|
||||
|
||||
public static String[] getAccountScreenNames(final Context context) {
|
||||
return getAccountScreenNames(context, false);
|
||||
}
|
||||
|
||||
public static String[] getAccountScreenNames(final Context context, final boolean includeAtChar) {
|
||||
return getAccountScreenNames(context, null, includeAtChar);
|
||||
return getAccountScreenNames(context, null);
|
||||
}
|
||||
|
||||
public static String[] getAccountScreenNames(final Context context, final long[] accountIds) {
|
||||
return getAccountScreenNames(context, accountIds, false);
|
||||
}
|
||||
|
||||
public static String[] getAccountScreenNames(final Context context, final long[] accountIds,
|
||||
final boolean includeAtChar) {
|
||||
if (context == null) return new String[0];
|
||||
final String[] cols = new String[]{Accounts.SCREEN_NAME};
|
||||
final String where = accountIds != null ? Expression.in(new Columns.Column(Accounts.ACCOUNT_ID),
|
||||
final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID),
|
||||
new RawItemArray(accountIds)).getSQL() : null;
|
||||
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where,
|
||||
null, null);
|
||||
@ -568,25 +372,8 @@ public class DataStoreUtils implements Constants {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getAllStatusesCount(final Context context, @NonNull final Uri uri) {
|
||||
if (context == null) return 0;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String table = getTableNameByUri(uri);
|
||||
if (table == null) return 0;
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, new String[]{Statuses.STATUS_ID},
|
||||
buildStatusFilterWhereClause(table, null).getSQL(),
|
||||
null, null);
|
||||
if (cur == null) return 0;
|
||||
try {
|
||||
return cur.getCount();
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStatusesCount(final Context context, final Uri uri, final long sinceId, final long... accountIds) {
|
||||
if (context == null) return 0;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final RawItemArray idsIn;
|
||||
if (accountIds == null || accountIds.length == 0 || (accountIds.length == 1 && accountIds[0] < 0)) {
|
||||
idsIn = new RawItemArray(getActivatedAccountIds(context));
|
||||
@ -594,7 +381,7 @@ public class DataStoreUtils implements Constants {
|
||||
idsIn = new RawItemArray(accountIds);
|
||||
}
|
||||
final Expression selection = Expression.and(
|
||||
Expression.in(new Columns.Column(Statuses.ACCOUNT_ID), idsIn),
|
||||
Expression.in(new Column(Statuses.ACCOUNT_ID), idsIn),
|
||||
Expression.greaterThan(Statuses.STATUS_ID, sinceId),
|
||||
buildStatusFilterWhereClause(getTableNameByUri(uri), null)
|
||||
);
|
||||
@ -618,7 +405,7 @@ public class DataStoreUtils implements Constants {
|
||||
} else {
|
||||
expressions = new Expression[3];
|
||||
}
|
||||
expressions[0] = Expression.in(new Columns.Column(Activities.ACCOUNT_ID), idsIn);
|
||||
expressions[0] = Expression.in(new Column(Activities.ACCOUNT_ID), idsIn);
|
||||
expressions[1] = Expression.greaterThan(Activities.TIMESTAMP, sinceTimestamp);
|
||||
expressions[2] = buildActivityFilterWhereClause(getTableNameByUri(uri), null);
|
||||
final Expression selection = Expression.and(expressions);
|
||||
@ -687,48 +474,48 @@ public class DataStoreUtils implements Constants {
|
||||
@NonNull
|
||||
public static Expression buildActivityFilterWhereClause(@NonNull final String table, final Expression extraSelection) {
|
||||
final SQLSelectQuery filteredUsersQuery = SQLQueryBuilder
|
||||
.select(new Columns.Column(new Table(Filters.Users.TABLE_NAME), Filters.Users.USER_ID))
|
||||
.select(new Column(new Table(Filters.Users.TABLE_NAME), Filters.Users.USER_ID))
|
||||
.from(new Tables(Filters.Users.TABLE_NAME))
|
||||
.build();
|
||||
final Expression filteredUsersWhere = Expression.or(
|
||||
Expression.in(new Columns.Column(new Table(table), Activities.STATUS_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Columns.Column(new Table(table), Activities.STATUS_RETWEETED_BY_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Columns.Column(new Table(table), Activities.STATUS_QUOTED_USER_ID), filteredUsersQuery)
|
||||
Expression.in(new Column(new Table(table), Activities.STATUS_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Column(new Table(table), Activities.STATUS_RETWEETED_BY_USER_ID), filteredUsersQuery),
|
||||
Expression.in(new Column(new Table(table), Activities.STATUS_QUOTED_USER_ID), filteredUsersQuery)
|
||||
);
|
||||
final SQLSelectQuery.Builder filteredIdsQueryBuilder = SQLQueryBuilder
|
||||
.select(new Columns.Column(new Table(table), Activities._ID))
|
||||
.select(new Column(new Table(table), Activities._ID))
|
||||
.from(new Tables(table))
|
||||
.where(filteredUsersWhere)
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Activities._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Activities._ID)))
|
||||
.from(new Tables(table, Filters.Sources.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_SOURCE),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_QUOTE_SOURCE),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_QUOTE_SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'")
|
||||
))
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Activities._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Activities._ID)))
|
||||
.from(new Tables(table, Filters.Keywords.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_TEXT_PLAIN),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_QUOTE_TEXT_PLAIN),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_QUOTE_TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'")
|
||||
))
|
||||
.union()
|
||||
.select(new Columns(new Columns.Column(new Table(table), Activities._ID)))
|
||||
.select(new Columns(new Column(new Table(table), Activities._ID)))
|
||||
.from(new Tables(table, Filters.Links.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_TEXT_HTML),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_TEXT_HTML),
|
||||
"'%>%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%</a>%'"),
|
||||
Expression.likeRaw(new Columns.Column(new Table(table), Activities.STATUS_QUOTE_TEXT_HTML),
|
||||
Expression.likeRaw(new Column(new Table(table), Activities.STATUS_QUOTE_TEXT_HTML),
|
||||
"'%>%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%</a>%'")
|
||||
));
|
||||
final Expression filterExpression = Expression.or(
|
||||
Expression.notIn(new Columns.Column(new Table(table), Activities._ID), filteredIdsQueryBuilder.build()),
|
||||
Expression.equals(new Columns.Column(new Table(table), Activities.IS_GAP), 1)
|
||||
Expression.notIn(new Column(new Table(table), Activities._ID), filteredIdsQueryBuilder.build()),
|
||||
Expression.equals(new Column(new Table(table), Activities.IS_GAP), 1)
|
||||
);
|
||||
if (extraSelection != null) {
|
||||
return Expression.and(filterExpression, extraSelection);
|
||||
@ -759,7 +546,7 @@ public class DataStoreUtils implements Constants {
|
||||
public static int[] getAccountColors(final Context context, final long[] accountIds) {
|
||||
if (context == null || accountIds == null) return new int[0];
|
||||
final String[] cols = new String[]{Accounts.ACCOUNT_ID, Accounts.COLOR};
|
||||
final String where = Expression.in(new Columns.Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL();
|
||||
final String where = Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL();
|
||||
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where,
|
||||
null, null);
|
||||
if (cur == null) return new int[0];
|
||||
@ -823,6 +610,7 @@ public class DataStoreUtils implements Constants {
|
||||
if (context == null) return false;
|
||||
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
|
||||
new String[]{SQLFunctions.COUNT()}, null, null, null);
|
||||
if (cur == null) return false;
|
||||
try {
|
||||
cur.moveToFirst();
|
||||
return cur.getInt(0) > 0;
|
||||
@ -850,7 +638,115 @@ public class DataStoreUtils implements Constants {
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getAccountNames(final Context context) {
|
||||
return getAccountScreenNames(context, null);
|
||||
public static synchronized void cleanDatabasesByItemLimit(final Context context) {
|
||||
if (context == null) return;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final int itemLimit = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).getInt(
|
||||
KEY_DATABASE_ITEM_LIMIT, DEFAULT_DATABASE_ITEM_LIMIT);
|
||||
|
||||
for (final long accountId : getAccountIds(context)) {
|
||||
// Clean statuses.
|
||||
for (final Uri uri : STATUSES_URIS) {
|
||||
if (CachedStatuses.CONTENT_URI.equals(uri)) {
|
||||
continue;
|
||||
}
|
||||
final String table = getTableNameByUri(uri);
|
||||
final Expression account_where = 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);
|
||||
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 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);
|
||||
resolver.delete(uri, where.getSQL(), null);
|
||||
}
|
||||
}
|
||||
// Clean cached values.
|
||||
for (final Uri uri : CACHE_URIS) {
|
||||
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());
|
||||
resolver.delete(uri, where.getSQL(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearAccountColor() {
|
||||
sAccountColors.clear();
|
||||
}
|
||||
|
||||
public static void clearAccountName() {
|
||||
sAccountScreenNames.clear();
|
||||
}
|
||||
|
||||
public static boolean isFilteringUser(Context context, long userId) {
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
final Expression where = Expression.equals(Filters.Users.USER_ID, userId);
|
||||
final Cursor c = cr.query(Filters.Users.CONTENT_URI, new String[0], where.getSQL(), null, null);
|
||||
if (c == null) return false;
|
||||
try {
|
||||
return c.getCount() > 0;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static long[] getLongFieldArray(@NonNull Context context, @NonNull Uri uri, @NonNull long[] keys,
|
||||
@NonNull String keyField, @NonNull String valueField,
|
||||
@Nullable OrderBy sortExpression) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final long[] messageIds = new long[keys.length];
|
||||
Arrays.fill(messageIds, -1);
|
||||
final String[] selectionArgs = TwidereArrayUtils.toStringArray(keys);
|
||||
final SQLSelectQuery.Builder builder = SQLQueryBuilder.select(new Columns(keyField, valueField))
|
||||
.from(new Table(getTableNameByUri(uri)))
|
||||
.groupBy(new Column(keyField))
|
||||
.having(Expression.in(new Column(keyField), new ArgsArray(keys.length)));
|
||||
if (sortExpression != null) {
|
||||
builder.orderBy(sortExpression);
|
||||
}
|
||||
final Cursor cur = ContentResolverUtils.query(resolver,
|
||||
Uri.withAppendedPath(TwidereDataStore.CONTENT_URI_DATABASE_READY, builder.buildSQL()),
|
||||
null, null, selectionArgs, null);
|
||||
if (cur == null) return messageIds;
|
||||
while (cur.moveToNext()) {
|
||||
final long accountId = cur.getLong(0);
|
||||
int idx = ArrayUtils.indexOf(keys, accountId);
|
||||
if (idx < 0) continue;
|
||||
messageIds[idx] = cur.getLong(1);
|
||||
}
|
||||
return messageIds;
|
||||
}
|
||||
|
||||
static int queryCount(@NonNull final Context context, @NonNull final Uri uri,
|
||||
@Nullable final String selection, @Nullable final String[] selectionArgs) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] projection = new String[]{SQLFunctions.COUNT()};
|
||||
final Cursor cur = ContentResolverUtils.query(resolver, uri, projection, selection, selectionArgs, null);
|
||||
if (cur == null) return -1;
|
||||
try {
|
||||
if (cur.moveToFirst()) {
|
||||
return cur.getInt(0);
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,15 +48,13 @@ public class TwidereListUtils {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String toStringForSQL(final List<String> list) {
|
||||
final int size = list != null ? list.size() : 0;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i > 0) {
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append('?');
|
||||
public static String[] toStringArray(final List<?> list) {
|
||||
if (list == null) return null;
|
||||
final int length = list.size();
|
||||
final String[] stringArray = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
stringArray[i] = ParseUtils.parseString(list.get(i));
|
||||
}
|
||||
return builder.toString();
|
||||
return stringArray;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
@ -119,11 +118,7 @@ import org.mariotaku.sqliteqb.library.AllColumns;
|
||||
import org.mariotaku.sqliteqb.library.Columns;
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column;
|
||||
import org.mariotaku.sqliteqb.library.Expression;
|
||||
import org.mariotaku.sqliteqb.library.OrderBy;
|
||||
import org.mariotaku.sqliteqb.library.RawItemArray;
|
||||
import org.mariotaku.sqliteqb.library.Selectable;
|
||||
import org.mariotaku.sqliteqb.library.Tables;
|
||||
import org.mariotaku.sqliteqb.library.query.SQLSelectQuery;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
@ -239,7 +234,6 @@ import edu.tsinghua.hotmobi.model.NotificationEvent;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
import static android.text.format.DateUtils.getRelativeTimeSpanString;
|
||||
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;
|
||||
import static org.mariotaku.twidere.util.TwidereLinkify.PATTERN_TWITTER_PROFILE_IMAGES;
|
||||
@ -407,19 +401,6 @@ public final class Utils implements Constants {
|
||||
accessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
public static String buildActivatedStatsWhereClause(final Context context, final String selection) {
|
||||
if (context == null) return null;
|
||||
final long[] account_ids = DataStoreUtils.getActivatedAccountIds(context);
|
||||
final Expression accountWhere = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids));
|
||||
final Expression where;
|
||||
if (selection != null) {
|
||||
where = Expression.and(accountWhere, new Expression(selection));
|
||||
} else {
|
||||
where = accountWhere;
|
||||
}
|
||||
return where.getSQL();
|
||||
}
|
||||
|
||||
public static Uri buildDirectMessageConversationUri(final long account_id, final long conversation_id,
|
||||
final String screen_name) {
|
||||
if (conversation_id <= 0 && screen_name == null) return TwidereDataStore.CONTENT_URI_NULL;
|
||||
@ -442,62 +423,6 @@ public final class Utils implements Constants {
|
||||
return !pm.queryIntentActivities(intent, 0).isEmpty();
|
||||
}
|
||||
|
||||
public static synchronized void cleanDatabasesByItemLimit(final Context context) {
|
||||
if (context == null) return;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final int itemLimit = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).getInt(
|
||||
KEY_DATABASE_ITEM_LIMIT, DEFAULT_DATABASE_ITEM_LIMIT);
|
||||
|
||||
for (final long accountId : DataStoreUtils.getAccountIds(context)) {
|
||||
// Clean statuses.
|
||||
for (final Uri uri : STATUSES_URIS) {
|
||||
if (CachedStatuses.CONTENT_URI.equals(uri)) {
|
||||
continue;
|
||||
}
|
||||
final String table = DataStoreUtils.getTableNameByUri(uri);
|
||||
final Expression account_where = 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);
|
||||
resolver.delete(uri, where.getSQL(), null);
|
||||
}
|
||||
for (final Uri uri : DIRECT_MESSAGES_URIS) {
|
||||
final String table = DataStoreUtils.getTableNameByUri(uri);
|
||||
final Expression account_where = 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);
|
||||
resolver.delete(uri, where.getSQL(), null);
|
||||
}
|
||||
}
|
||||
// Clean cached values.
|
||||
for (final Uri uri : CACHE_URIS) {
|
||||
final String table = DataStoreUtils.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());
|
||||
resolver.delete(uri, where.getSQL(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearAccountColor() {
|
||||
DataStoreUtils.sAccountColors.clear();
|
||||
}
|
||||
|
||||
public static void clearAccountName() {
|
||||
DataStoreUtils.sAccountScreenNames.clear();
|
||||
}
|
||||
|
||||
public static void clearListViewChoices(final AbsListView view) {
|
||||
if (view == null) return;
|
||||
final ListAdapter adapter = view.getAdapter();
|
||||
@ -2997,35 +2922,16 @@ public final class Utils implements Constants {
|
||||
final ListView listView = fragment.getListView();
|
||||
listView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
|
||||
listView.setClipToPadding(false);
|
||||
// if (listView instanceof RefreshNowListView) {
|
||||
// final View indicatorView = ((RefreshNowListView) listView).getRefreshIndicatorView();
|
||||
// final LayoutParams lp = indicatorView.getLayoutParams();
|
||||
// if (lp instanceof MarginLayoutParams) {
|
||||
// ((MarginLayoutParams) lp).topMargin = insets.top;
|
||||
// indicatorView.setLayoutParams(lp);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static boolean isFilteringUser(Context context, long userId) {
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
final Expression where = Expression.equals(Users.USER_ID, userId);
|
||||
final Cursor c = cr.query(Users.CONTENT_URI, new String[0], where.getSQL(), null, null);
|
||||
//noinspection TryFinallyCanBeTryWithResources
|
||||
try {
|
||||
return c.getCount() > 0;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ParcelableUser getUserForConversation(Context context, long accountId,
|
||||
long conversationId) {
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
final Expression where = Expression.and(Expression.equals(ConversationEntries.ACCOUNT_ID, accountId),
|
||||
Expression.equals(ConversationEntries.CONVERSATION_ID, conversationId));
|
||||
final Cursor c = cr.query(ConversationEntries.CONTENT_URI, null, where.getSQL(), null, null);
|
||||
//noinspection TryFinallyCanBeTryWithResources
|
||||
if (c == null) return null;
|
||||
try {
|
||||
if (c.moveToFirst()) return ParcelableUser.fromDirectMessageConversationEntry(c);
|
||||
} finally {
|
||||
|
@ -159,7 +159,7 @@ public final class DatabaseUpgradeHelper {
|
||||
notNullCols[count++] = column.getName();
|
||||
}
|
||||
}
|
||||
return TwidereArrayUtils.subArray(notNullCols, 0, count);
|
||||
return ArrayUtils.subarray(notNullCols, 0, count);
|
||||
}
|
||||
|
||||
private static Map<String, String> getTypeMapByCreateQuery(final String query) {
|
||||
|
@ -37,6 +37,7 @@ import org.mariotaku.inetaddrjni.library.InetAddressUtils;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.TwidereMathUtils;
|
||||
import org.xbill.DNS.AAAARecord;
|
||||
import org.xbill.DNS.ARecord;
|
||||
import org.xbill.DNS.DClass;
|
||||
@ -217,7 +218,7 @@ public class TwidereDns implements Constants, Dns {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (mConnnectTimeout == 0 || inetAddress.isReachable((int) mConnnectTimeout)) {
|
||||
if (mConnnectTimeout == 0 || inetAddress.isReachable(TwidereMathUtils.clamp((int) mConnnectTimeout / 2, 1000, 3000))) {
|
||||
resolvedAddresses.add(InetAddress.getByAddress(originalHost, inetAddress.getAddress()));
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,9 @@
|
||||
<org.mariotaku.twidere.view.ExtendedRecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<requestFocus/>
|
||||
</org.mariotaku.twidere.view.ExtendedRecyclerView>
|
||||
|
Loading…
x
Reference in New Issue
Block a user