kotlin migration
This commit is contained in:
parent
7e577b5957
commit
90cb5770e9
|
@ -34,7 +34,7 @@ subprojects {
|
|||
ext.kotlin_version = '1.0.6'
|
||||
ext.android_support_lib_version = '25.1.1'
|
||||
ext.mariotaku_commons_library_version = '0.9.11'
|
||||
ext.mariotaku_restfu_version = '0.9.34'
|
||||
ext.mariotaku_restfu_version = '0.9.35'
|
||||
ext.play_services_version = '10.0.1'
|
||||
ext.crashlyrics_version = '2.6.6'
|
||||
ext.fabric_plugin_version = '1.22.0'
|
||||
|
|
|
@ -98,7 +98,7 @@ public class SessionEvent extends BaseEvent implements Parcelable {
|
|||
|
||||
public void dumpPreferences(Context context) {
|
||||
final HashMap<String, String> preferences = new HashMap<>();
|
||||
for (AccountPreferences pref : AccountPreferences.getAccountPreferences(context, DataStoreUtils.getAccountKeys(context))) {
|
||||
for (AccountPreferences pref : AccountPreferences.getAccountPreferences(context, DataStoreUtils.INSTANCE.getAccountKeys(context))) {
|
||||
final UserKey accountKey = pref.getAccountKey();
|
||||
preferences.put("notification_" + accountKey + "_home", String.valueOf(pref.isHomeTimelineNotificationEnabled()));
|
||||
preferences.put("notification_" + accountKey + "_interactions", String.valueOf(pref.isInteractionsNotificationEnabled()));
|
||||
|
|
|
@ -166,7 +166,7 @@ public class NetworkDiagnosticsFragment extends BaseFragment {
|
|||
}
|
||||
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
|
||||
|
||||
for (UserKey accountKey : DataStoreUtils.getAccountKeys(mContext)) {
|
||||
for (UserKey accountKey : DataStoreUtils.INSTANCE.getAccountKeys(mContext)) {
|
||||
final AccountDetails details = AccountUtils.getAccountDetails(AccountManager.get(mContext), accountKey, true);
|
||||
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(mContext, accountKey);
|
||||
if (details == null || twitter == null) continue;
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.preference;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedStatuses;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Notifications;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
|
||||
|
||||
import static org.mariotaku.twidere.util.DataStoreUtils.CACHE_URIS;
|
||||
import static org.mariotaku.twidere.util.DataStoreUtils.DIRECT_MESSAGES_URIS;
|
||||
import static org.mariotaku.twidere.util.DataStoreUtils.STATUSES_URIS;
|
||||
|
||||
public class ClearDatabasesPreference extends AsyncTaskPreference implements Constants {
|
||||
|
||||
public ClearDatabasesPreference(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ClearDatabasesPreference(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.preferenceStyle);
|
||||
}
|
||||
|
||||
public ClearDatabasesPreference(final Context context, final AttributeSet attrs, final int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
for (final Uri uri : STATUSES_URIS) {
|
||||
if (CachedStatuses.CONTENT_URI.equals(uri)) {
|
||||
continue;
|
||||
}
|
||||
resolver.delete(uri, null, null);
|
||||
}
|
||||
for (final Uri uri : DIRECT_MESSAGES_URIS) {
|
||||
resolver.delete(uri, null, null);
|
||||
}
|
||||
for (final Uri uri : CACHE_URIS) {
|
||||
resolver.delete(uri, null, null);
|
||||
}
|
||||
resolver.delete(Activities.AboutMe.CONTENT_URI, null, null);
|
||||
resolver.delete(Activities.ByFriends.CONTENT_URI, null, null);
|
||||
resolver.delete(Notifications.CONTENT_URI, null, null);
|
||||
resolver.delete(UnreadCounts.CONTENT_URI, null, null);
|
||||
resolver.delete(SavedSearches.CONTENT_URI, null, null);
|
||||
|
||||
final SharedPreferences prefs = context.getSharedPreferences(TIMELINE_POSITIONS_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
}
|
|
@ -68,7 +68,6 @@ import org.mariotaku.sqliteqb.library.SQLConstants;
|
|||
import org.mariotaku.sqliteqb.library.SQLFunctions;
|
||||
import org.mariotaku.sqliteqb.library.query.SQLSelectQuery;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.activity.HomeActivity;
|
||||
import org.mariotaku.twidere.annotation.CustomTabType;
|
||||
|
@ -140,7 +139,72 @@ import java.util.regex.Pattern;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public final class TwidereDataProvider extends ContentProvider implements Constants,
|
||||
import static org.mariotaku.twidere.TwidereConstants.AUTHORITY_DRAFTS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.AUTHORITY_INTERACTIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.BROADCAST_NOTIFICATION_DELETED;
|
||||
import static org.mariotaku.twidere.TwidereConstants.INTENT_ACTION_DISCARD_DRAFT;
|
||||
import static org.mariotaku.twidere.TwidereConstants.INTENT_ACTION_SEND_DRAFT;
|
||||
import static org.mariotaku.twidere.TwidereConstants.KEY_COMBINED_NOTIFICATIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.KEY_I_WANT_MY_STARS_BACK;
|
||||
import static org.mariotaku.twidere.TwidereConstants.KEY_NAME_FIRST;
|
||||
import static org.mariotaku.twidere.TwidereConstants.LOGTAG;
|
||||
import static org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_DIRECT_MESSAGES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_DRAFTS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_HOME_TIMELINE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_INTERACTIONS_TIMELINE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.PERMISSION_DIRECT_MESSAGES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.PERMISSION_PREFERENCES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.PERMISSION_READ;
|
||||
import static org.mariotaku.twidere.TwidereConstants.PERMISSION_WRITE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_ACCOUNT_KEY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_FROM_NOTIFICATION;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFICATION_TYPE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_QUERY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_READ_POSITION;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_READ_POSITIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_TIMESTAMP;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_TYPE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_URL;
|
||||
import static org.mariotaku.twidere.TwidereConstants.SCHEME_TWIDERE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_ACTIVITIES_ABOUT_ME;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_CACHED_HASHTAGS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_CACHED_RELATIONSHIPS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_CACHED_STATUSES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_CACHED_USERS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_DRAFTS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_FILTERED_KEYWORDS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_FILTERED_LINKS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_FILTERED_SOURCES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_FILTERED_USERS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_MENTIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_MESSAGES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_MESSAGES_CONVERSATIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_SEARCH_HISTORY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_STATUSES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_TABS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.TABLE_ID_TRENDS_LOCAL;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_ALL_PREFERENCES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_CACHED_IMAGES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_CACHED_USERS_WITH_RELATIONSHIP;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_CACHED_USERS_WITH_SCORE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_CACHE_FILES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_DATABASE_PREPARE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_DNS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_DRAFTS_UNSENT;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_EMPTY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_NOTIFICATIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_NULL;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_PERMISSIONS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_PREFERENCES;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_RAW_QUERY;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_SUGGESTIONS_AUTO_COMPLETE;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_SUGGESTIONS_SEARCH;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_UNREAD_COUNTS;
|
||||
import static org.mariotaku.twidere.TwidereConstants.VIRTUAL_TABLE_ID_UNREAD_COUNTS_BY_TYPE;
|
||||
|
||||
public final class TwidereDataProvider extends ContentProvider implements
|
||||
OnSharedPreferenceChangeListener, LazyLoadCallback {
|
||||
|
||||
public static final String TAG_OLDEST_MESSAGES = "oldest_messages";
|
||||
|
@ -330,8 +394,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
}
|
||||
|
||||
private int bulkInsertInternal(@NonNull Uri uri, @NonNull ContentValues[] valuesArray) {
|
||||
final int tableId = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(tableId);
|
||||
final int tableId = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(tableId);
|
||||
checkWritePermission(tableId, table);
|
||||
int result = 0;
|
||||
final long[] newIds = new long[valuesArray.length];
|
||||
|
@ -394,8 +458,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
}
|
||||
|
||||
private int deleteInternal(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
final int tableId = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(tableId);
|
||||
final int tableId = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(tableId);
|
||||
checkWritePermission(tableId, table);
|
||||
switch (tableId) {
|
||||
case VIRTUAL_TABLE_ID_NOTIFICATIONS: {
|
||||
|
@ -446,8 +510,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
}
|
||||
|
||||
private Uri insertInternal(@NonNull Uri uri, ContentValues values) {
|
||||
final int tableId = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(tableId);
|
||||
final int tableId = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(tableId);
|
||||
checkWritePermission(tableId, table);
|
||||
final long rowId;
|
||||
switch (tableId) {
|
||||
|
@ -596,8 +660,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull final Uri uri, @NonNull final String mode) throws FileNotFoundException {
|
||||
final int table_id = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(table_id);
|
||||
final int table_id = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(table_id);
|
||||
final int modeCode;
|
||||
switch (mode) {
|
||||
case "r":
|
||||
|
@ -633,8 +697,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
public Cursor query(@NonNull final Uri uri, final String[] projection, final String selection, final String[] selectionArgs,
|
||||
final String sortOrder) {
|
||||
try {
|
||||
final int tableId = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(tableId);
|
||||
final int tableId = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(tableId);
|
||||
checkReadPermission(tableId, table, projection);
|
||||
switch (tableId) {
|
||||
case VIRTUAL_TABLE_ID_DATABASE_PREPARE: {
|
||||
|
@ -908,8 +972,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
}
|
||||
|
||||
private int updateInternal(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
final int tableId = DataStoreUtils.getTableId(uri);
|
||||
final String table = DataStoreUtils.getTableNameById(tableId);
|
||||
final int tableId = DataStoreUtils.INSTANCE.getTableId(uri);
|
||||
final String table = DataStoreUtils.INSTANCE.getTableNameById(tableId);
|
||||
checkWritePermission(tableId, table);
|
||||
int result = 0;
|
||||
if (table != null) {
|
||||
|
@ -1120,7 +1184,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
@Override
|
||||
public void run() {
|
||||
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
||||
DataStoreUtils.getAccountKeys(context));
|
||||
DataStoreUtils.INSTANCE.getAccountKeys(context));
|
||||
for (final AccountPreferences pref : prefs) {
|
||||
if (!pref.isHomeTimelineNotificationEnabled()) continue;
|
||||
final long positionTag = getPositionTag(CustomTabType.HOME_TIMELINE, pref.getAccountKey());
|
||||
|
@ -1136,7 +1200,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
@Override
|
||||
public void run() {
|
||||
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
||||
DataStoreUtils.getAccountKeys(context));
|
||||
DataStoreUtils.INSTANCE.getAccountKeys(context));
|
||||
final boolean combined = preferences.getBoolean(KEY_COMBINED_NOTIFICATIONS);
|
||||
for (final AccountPreferences pref : prefs) {
|
||||
if (!pref.isInteractionsNotificationEnabled()) continue;
|
||||
|
@ -1150,7 +1214,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
}
|
||||
case TABLE_ID_MESSAGES: {
|
||||
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
||||
DataStoreUtils.getAccountKeys(context));
|
||||
DataStoreUtils.INSTANCE.getAccountKeys(context));
|
||||
for (final AccountPreferences pref : prefs) {
|
||||
if (!pref.isDirectMessagesNotificationEnabled()) continue;
|
||||
final StringLongPair[] pairs = readStateManager.getPositionPairs(CustomTabType.DIRECT_MESSAGES);
|
||||
|
@ -1266,7 +1330,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
applyNotificationPreferences(builder, pref, pref.getMentionsNotificationType());
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
final String accountName = DataStoreUtils.getAccountDisplayName(context, accountKey, nameFirst);
|
||||
final String accountName = DataStoreUtils.INSTANCE.getAccountDisplayName(context, accountKey, nameFirst);
|
||||
builder.setContentText(accountName);
|
||||
final InboxStyle style = new InboxStyle();
|
||||
builder.setStyle(style);
|
||||
|
@ -1296,7 +1360,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
|
|||
activity.status_quoted_user_key)) {
|
||||
continue;
|
||||
}
|
||||
final UserKey[] filteredUserIds = DataStoreUtils.getFilteredUserIds(context);
|
||||
final UserKey[] filteredUserIds = DataStoreUtils.INSTANCE.getFilteredUserIds(context);
|
||||
if (timestamp == -1) {
|
||||
timestamp = activity.timestamp;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.task;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v4.util.SimpleArrayMap;
|
||||
|
||||
import org.mariotaku.twidere.model.UserKey;
|
||||
import org.mariotaku.twidere.util.TwitterWrapper;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/10.
|
||||
*/
|
||||
public final class RemoveUnreadCountsTask extends AsyncTask<Object, Object, Integer> {
|
||||
private final Context context;
|
||||
private final int position;
|
||||
private final SimpleArrayMap<UserKey, Set<String>> counts;
|
||||
|
||||
public RemoveUnreadCountsTask(Context context, final int position, final SimpleArrayMap<UserKey, Set<String>> counts) {
|
||||
this.context = context;
|
||||
this.position = position;
|
||||
this.counts = counts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(final Object... params) {
|
||||
return TwitterWrapper.removeUnreadCounts(context, position, counts);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,6 @@ import android.content.pm.ApplicationInfo;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
@ -53,7 +52,6 @@ import android.support.annotation.DrawableRes;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||
|
@ -80,10 +78,8 @@ import android.widget.Toast;
|
|||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.json.JSONException;
|
||||
import org.mariotaku.microblog.library.MicroBlog;
|
||||
import org.mariotaku.microblog.library.MicroBlogException;
|
||||
import org.mariotaku.microblog.library.twitter.model.RateLimitStatus;
|
||||
import org.mariotaku.microblog.library.twitter.model.Status;
|
||||
import org.mariotaku.pickncrop.library.PNCUtils;
|
||||
import org.mariotaku.sqliteqb.library.AllColumns;
|
||||
import org.mariotaku.sqliteqb.library.Columns;
|
||||
|
@ -98,17 +94,12 @@ import org.mariotaku.twidere.graphic.PaddingDrawable;
|
|||
import org.mariotaku.twidere.model.AccountDetails;
|
||||
import org.mariotaku.twidere.model.AccountPreferences;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.model.ParcelableStatusCursorIndices;
|
||||
import org.mariotaku.twidere.model.ParcelableStatusValuesCreator;
|
||||
import org.mariotaku.twidere.model.ParcelableUserMention;
|
||||
import org.mariotaku.twidere.model.PebbleMessage;
|
||||
import org.mariotaku.twidere.model.UserKey;
|
||||
import org.mariotaku.twidere.model.util.AccountUtils;
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedStatuses;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.view.TabPagerIndicator;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
@ -126,7 +117,6 @@ import java.util.regex.Pattern;
|
|||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
import edu.tsinghua.hotmobi.model.NotificationEvent;
|
||||
|
||||
import static org.mariotaku.twidere.util.DataStoreUtils.STATUSES_URIS;
|
||||
import static org.mariotaku.twidere.util.TwidereLinkify.PATTERN_TWITTER_PROFILE_IMAGES;
|
||||
|
||||
public final class Utils implements Constants {
|
||||
|
@ -310,7 +300,7 @@ public final class Utils implements Constants {
|
|||
} catch (NumberFormatException e) {
|
||||
// Ignore
|
||||
}
|
||||
final UserKey accountKey = DataStoreUtils.findAccountKey(context, accountId);
|
||||
final UserKey accountKey = DataStoreUtils.INSTANCE.findAccountKey(context, accountId);
|
||||
args.putParcelable(EXTRA_ACCOUNT_KEY, accountKey);
|
||||
if (accountKey == null) return new UserKey[]{new UserKey(accountId, null)};
|
||||
return new UserKey[]{accountKey};
|
||||
|
@ -332,59 +322,6 @@ public final class Utils implements Constants {
|
|||
return accountKey + ":" + tag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@WorkerThread
|
||||
public static ParcelableStatus findStatus(@NonNull final Context context,
|
||||
@NonNull final UserKey accountKey,
|
||||
@NonNull final String statusId)
|
||||
throws MicroBlogException {
|
||||
final ParcelableStatus cached = findStatusInDatabases(context, accountKey, statusId);
|
||||
if (cached != null) return cached;
|
||||
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(context, accountKey);
|
||||
if (twitter == null) throw new MicroBlogException("Account does not exist");
|
||||
final Status result = twitter.showStatus(statusId);
|
||||
final String where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Statuses.STATUS_ID)).getSQL();
|
||||
final String[] whereArgs = {accountKey.toString(), statusId};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final ParcelableStatus status = ParcelableStatusUtils.INSTANCE.fromStatus(result, accountKey, false);
|
||||
resolver.delete(CachedStatuses.CONTENT_URI, where, whereArgs);
|
||||
try {
|
||||
resolver.insert(CachedStatuses.CONTENT_URI, ParcelableStatusValuesCreator.create(status));
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@WorkerThread
|
||||
public static ParcelableStatus findStatusInDatabases(@NonNull final Context context,
|
||||
@NonNull final UserKey accountKey,
|
||||
@NonNull final String statusId) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
ParcelableStatus status = null;
|
||||
final String where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Statuses.STATUS_ID)).getSQL();
|
||||
final String[] whereArgs = {accountKey.toString(), statusId};
|
||||
for (final Uri uri : STATUSES_URIS) {
|
||||
final Cursor cur = resolver.query(uri, Statuses.COLUMNS, where, whereArgs, null);
|
||||
if (cur == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (cur.getCount() > 0 && cur.moveToFirst()) {
|
||||
status = ParcelableStatusCursorIndices.fromCursor(cur);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
} finally {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String formatSameDayTime(final Context context, final long timestamp) {
|
||||
if (context == null) return null;
|
||||
|
@ -461,7 +398,7 @@ public final class Utils implements Constants {
|
|||
Context.MODE_PRIVATE);
|
||||
final String string = prefs.getString(KEY_DEFAULT_ACCOUNT_KEY, null);
|
||||
UserKey accountKey = string != null ? UserKey.valueOf(string) : null;
|
||||
final UserKey[] accountKeys = DataStoreUtils.getAccountKeys(context);
|
||||
final UserKey[] accountKeys = DataStoreUtils.INSTANCE.getAccountKeys(context);
|
||||
int idMatchIdx = -1;
|
||||
for (int i = 0, accountIdsLength = accountKeys.length; i < accountIdsLength; i++) {
|
||||
if (accountKeys[i].equals(accountKey)) {
|
||||
|
@ -715,7 +652,7 @@ public final class Utils implements Constants {
|
|||
}
|
||||
|
||||
public static boolean hasAutoRefreshAccounts(final Context context) {
|
||||
final UserKey[] accountKeys = DataStoreUtils.getAccountKeys(context);
|
||||
final UserKey[] accountKeys = DataStoreUtils.INSTANCE.getAccountKeys(context);
|
||||
return !ArrayUtils.isEmpty(AccountPreferences.getAutoRefreshEnabledAccountIds(context, accountKeys));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,3 +13,7 @@ fun Array<*>?.isNullOrEmpty(): Boolean {
|
|||
return this == null || this.isEmpty()
|
||||
}
|
||||
|
||||
fun <T : Any> Array<T>.toNulls(): Array<T?> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return this as Array<T?>
|
||||
}
|
|
@ -40,6 +40,7 @@ val attachLocationKey = KBooleanKey(KEY_ATTACH_LOCATION, false)
|
|||
val attachPreciseLocationKey = KBooleanKey(KEY_ATTACH_PRECISE_LOCATION, false)
|
||||
val noCloseAfterTweetSentKey = KBooleanKey(KEY_NO_CLOSE_AFTER_TWEET_SENT, false)
|
||||
val loadItemLimitKey = KIntKey(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT)
|
||||
val databaseItemLimitKey = KIntKey(KEY_DATABASE_ITEM_LIMIT, DEFAULT_DATABASE_ITEM_LIMIT)
|
||||
val defaultFeatureLastUpdated = KLongKey("default_feature_last_updated", -1)
|
||||
val drawerTutorialCompleted = KBooleanKey(KEY_SETTINGS_WIZARD_COMPLETED, false)
|
||||
val stopAutoRefreshWhenBatteryLowKey = KBooleanKey(KEY_STOP_AUTO_REFRESH_WHEN_BATTERY_LOW, true)
|
||||
|
|
|
@ -32,8 +32,8 @@ import android.widget.Toast
|
|||
import com.squareup.otto.Subscribe
|
||||
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.sqliteqb.library.ArgsArray
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
|
@ -77,15 +77,12 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
showContentOrError()
|
||||
}
|
||||
|
||||
override fun onCreateActivitiesLoader(context: Context,
|
||||
args: Bundle,
|
||||
fromUser: Boolean): Loader<List<ParcelableActivity>> {
|
||||
override fun onCreateActivitiesLoader(context: Context, args: Bundle, fromUser: Boolean): Loader<List<ParcelableActivity>> {
|
||||
val uri = contentUri
|
||||
val table = getTableNameByUri(uri)
|
||||
val table = getTableNameByUri(uri)!!
|
||||
val sortOrder = sortOrder
|
||||
val accountKeys = accountKeys
|
||||
val accountWhere = Expression.`in`(Column(Activities.ACCOUNT_KEY),
|
||||
ArgsArray(accountKeys.size))
|
||||
val accountWhere = Expression.inArgs(Column(Activities.ACCOUNT_KEY), accountKeys.size)
|
||||
val filterWhere = getFiltersWhere(table)
|
||||
val where: Expression
|
||||
if (filterWhere != null) {
|
||||
|
@ -147,8 +144,8 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
super.onLoadMoreContents(position)
|
||||
if (position == 0L) return
|
||||
getActivities(object : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
return this@CursorActivitiesFragment.accountKeys
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
this@CursorActivitiesFragment.accountKeys
|
||||
}
|
||||
|
||||
override val maxIds: Array<String?>?
|
||||
|
@ -158,7 +155,7 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
get() {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getOldestActivityMaxSortPositions(context,
|
||||
contentUri, accountKeys)
|
||||
contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
override val hasMaxIds: Boolean
|
||||
|
@ -172,8 +169,8 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
override fun triggerRefresh(): Boolean {
|
||||
super.triggerRefresh()
|
||||
getActivities(object : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
return this@CursorActivitiesFragment.accountKeys
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
this@CursorActivitiesFragment.accountKeys
|
||||
}
|
||||
|
||||
override val sinceIds: Array<String?>?
|
||||
|
@ -183,7 +180,7 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
get() {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getNewestActivityMaxSortPositions(context,
|
||||
contentUri, accountKeys)
|
||||
contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
override val hasSinceIds: Boolean
|
||||
|
@ -202,7 +199,7 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
|
||||
protected fun getNewestActivityIds(accountKeys: Array<UserKey>): Array<String?>? {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getNewestActivityMaxPositions(context, contentUri, accountKeys)
|
||||
return DataStoreUtils.getNewestActivityMaxPositions(context, contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
protected abstract val notificationType: Int
|
||||
|
@ -220,7 +217,7 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
|||
|
||||
protected fun getOldestActivityIds(accountKeys: Array<UserKey>): Array<String?>? {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getOldestActivityMaxPositions(context, contentUri, accountKeys)
|
||||
return DataStoreUtils.getOldestActivityMaxPositions(context, contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
protected abstract val isFilterEnabled: Boolean
|
||||
|
|
|
@ -32,7 +32,7 @@ import com.squareup.otto.Subscribe
|
|||
import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
|
||||
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
|
||||
import org.mariotaku.sqliteqb.library.ArgsArray
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
|
@ -79,11 +79,10 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
|
||||
override fun onCreateStatusesLoader(context: Context, args: Bundle, fromUser: Boolean): Loader<List<ParcelableStatus>?> {
|
||||
val uri = contentUri
|
||||
val table = DataStoreUtils.getTableNameByUri(uri)
|
||||
val table = DataStoreUtils.getTableNameByUri(uri)!!
|
||||
val sortOrder = Statuses.DEFAULT_SORT_ORDER
|
||||
val accountKeys = this.accountKeys
|
||||
val accountWhere = Expression.`in`(Column(Statuses.ACCOUNT_KEY),
|
||||
ArgsArray(accountKeys.size))
|
||||
val accountWhere = Expression.inArgs(Column(Statuses.ACCOUNT_KEY), accountKeys.size)
|
||||
val filterWhere = getFiltersWhere(table)
|
||||
val where: Expression
|
||||
if (filterWhere != null) {
|
||||
|
@ -178,8 +177,8 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
super.onLoadMoreContents(position)
|
||||
if (position == 0L) return
|
||||
getStatuses(object : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
return this@CursorStatusesFragment.accountKeys
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
this@CursorStatusesFragment.accountKeys
|
||||
}
|
||||
|
||||
override val maxIds: Array<String?>?
|
||||
|
@ -189,7 +188,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
get() {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getOldestStatusSortIds(context, contentUri,
|
||||
accountKeys)
|
||||
accountKeys.toNulls())
|
||||
}
|
||||
|
||||
override val hasMaxIds: Boolean
|
||||
|
@ -203,8 +202,8 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
override fun triggerRefresh(): Boolean {
|
||||
super.triggerRefresh()
|
||||
getStatuses(object : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
return this@CursorStatusesFragment.accountKeys
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
this@CursorStatusesFragment.accountKeys
|
||||
}
|
||||
|
||||
override val hasMaxIds: Boolean
|
||||
|
@ -214,7 +213,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
get() = getNewestStatusIds(accountKeys)
|
||||
|
||||
override val sinceSortIds: LongArray?
|
||||
get() = DataStoreUtils.getNewestStatusSortIds(context, contentUri, accountKeys)
|
||||
get() = DataStoreUtils.getNewestStatusSortIds(context, contentUri, accountKeys.toNulls())
|
||||
|
||||
override val shouldAbort: Boolean
|
||||
get() = context == null
|
||||
|
@ -229,7 +228,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
|
||||
protected fun getNewestStatusIds(accountKeys: Array<UserKey>): Array<String?>? {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getNewestStatusIds(context, contentUri, accountKeys)
|
||||
return DataStoreUtils.getNewestStatusIds(context, contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
|
@ -245,7 +244,7 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
|
|||
|
||||
protected fun getOldestStatusIds(accountKeys: Array<UserKey>): Array<String?>? {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getOldestStatusIds(context, contentUri, accountKeys)
|
||||
return DataStoreUtils.getOldestStatusIds(context, contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
protected open fun processWhere(where: Expression, whereArgs: Array<String>): ParameterizedExpression {
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package org.mariotaku.twidere.fragment
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.Loader
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.sqliteqb.library.OrderBy
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.MessagesEntriesAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.constant.newDocumentApiKey
|
||||
import org.mariotaku.twidere.extension.model.user
|
||||
import org.mariotaku.twidere.loader.ObjectCursorLoader
|
||||
|
@ -16,6 +19,8 @@ import org.mariotaku.twidere.model.ParcelableMessageConversation
|
|||
import org.mariotaku.twidere.model.ParcelableMessageConversationCursorIndices
|
||||
import org.mariotaku.twidere.model.SimpleRefreshTaskParam
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
|
@ -65,9 +70,36 @@ class MessagesEntriesFragment : AbsContentListRecyclerViewFragment<MessagesEntri
|
|||
override fun triggerRefresh(): Boolean {
|
||||
super.triggerRefresh()
|
||||
twitterWrapper.getMessagesAsync(object : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
return this@MessagesEntriesFragment.accountKeys
|
||||
private val accounts by lazy {
|
||||
AccountUtils.getAllAccountDetails(AccountManager.get(context), accountKeys, false)
|
||||
}
|
||||
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
this@MessagesEntriesFragment.accountKeys
|
||||
}
|
||||
|
||||
override val sinceIds: Array<String?>?
|
||||
get() {
|
||||
val result = arrayOfNulls<String>(accountKeys.size)
|
||||
val hasSinceAccountKeys = accounts.mapNotNull { account ->
|
||||
when (account?.type) {
|
||||
AccountType.FANFOU -> {
|
||||
return@mapNotNull null
|
||||
}
|
||||
}
|
||||
return@mapNotNull account?.key
|
||||
}.toTypedArray()
|
||||
val incomingIds = DataStoreUtils.getMessageIds(context, Messages.CONTENT_URI,
|
||||
hasSinceAccountKeys.toNulls(), false)
|
||||
val outgoingIds = DataStoreUtils.getMessageIds(context, Messages.CONTENT_URI,
|
||||
hasSinceAccountKeys.toNulls(), true)
|
||||
loop@ for (idx in 0..accountKeys.lastIndex) {
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
||||
override val hasSinceIds: Boolean = true
|
||||
override val hasMaxIds: Boolean = false
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.nfc.NdefRecord
|
|||
import android.nfc.NfcAdapter.CreateNdefMessageCallback
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks
|
||||
import android.support.v4.content.AsyncTaskLoader
|
||||
import android.support.v4.content.FixedAsyncTaskLoader
|
||||
import android.support.v4.content.Loader
|
||||
import android.support.v7.app.AlertDialog
|
||||
|
@ -329,42 +328,31 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
|
|||
|
||||
class EditUserListDialogFragment : BaseDialogFragment(), DialogInterface.OnClickListener {
|
||||
|
||||
private var mName: String? = null
|
||||
private var mDescription: String? = null
|
||||
private var mAccountKey: UserKey? = null
|
||||
private var mListId: String? = null
|
||||
private var mIsPublic: Boolean = false
|
||||
private val accountKey by lazy { arguments.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY) }
|
||||
private val listId: String by lazy { arguments.getString(EXTRA_LIST_ID) }
|
||||
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> {
|
||||
val alertDialog = dialog as AlertDialog
|
||||
val editName = alertDialog.findViewById(R.id.name) as MaterialEditText?
|
||||
val editDescription = alertDialog.findViewById(R.id.description) as MaterialEditText?
|
||||
val editIsPublic = alertDialog.findViewById(R.id.is_public) as CheckBox?
|
||||
assert(editName != null && editDescription != null && editIsPublic != null)
|
||||
val name = ParseUtils.parseString(editName!!.text)
|
||||
val description = ParseUtils.parseString(editDescription!!.text)
|
||||
val isPublic = editIsPublic!!.isChecked
|
||||
val editName = alertDialog.findViewById(R.id.name) as MaterialEditText
|
||||
val editDescription = alertDialog.findViewById(R.id.description) as MaterialEditText
|
||||
val editIsPublic = alertDialog.findViewById(R.id.is_public) as CheckBox
|
||||
val name = ParseUtils.parseString(editName.text)
|
||||
val description = ParseUtils.parseString(editDescription.text)
|
||||
val isPublic = editIsPublic.isChecked
|
||||
if (TextUtils.isEmpty(name)) return
|
||||
val update = UserListUpdate()
|
||||
update.setMode(if (isPublic) UserList.Mode.PUBLIC else UserList.Mode.PRIVATE)
|
||||
update.setName(name)
|
||||
update.setDescription(description)
|
||||
twitterWrapper.updateUserListDetails(mAccountKey, mListId, update)
|
||||
twitterWrapper.updateUserListDetails(accountKey, listId, update)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val bundle = savedInstanceState ?: arguments
|
||||
mAccountKey = bundle?.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||
mListId = bundle?.getString(EXTRA_LIST_ID)
|
||||
mName = bundle?.getString(EXTRA_LIST_NAME)
|
||||
mDescription = bundle?.getString(EXTRA_DESCRIPTION)
|
||||
mIsPublic = bundle == null || bundle.getBoolean(EXTRA_IS_PUBLIC, true)
|
||||
val context = activity
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder.setView(R.layout.dialog_user_list_detail_editor)
|
||||
builder.setTitle(R.string.user_list)
|
||||
|
@ -374,31 +362,19 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
|
|||
dialog.setOnShowListener { dialog ->
|
||||
dialog as AlertDialog
|
||||
dialog.applyTheme()
|
||||
val editName = dialog.findViewById(R.id.name) as MaterialEditText?
|
||||
val editDescription = dialog.findViewById(R.id.description) as MaterialEditText?
|
||||
val editPublic = dialog.findViewById(R.id.is_public) as CheckBox?
|
||||
assert(editName != null && editDescription != null && editPublic != null)
|
||||
editName!!.addValidator(UserListNameValidator(getString(R.string.invalid_list_name)))
|
||||
if (mName != null) {
|
||||
editName.setText(mName)
|
||||
val editName = dialog.findViewById(R.id.name) as MaterialEditText
|
||||
val editDescription = dialog.findViewById(R.id.description) as MaterialEditText
|
||||
val editPublic = dialog.findViewById(R.id.is_public) as CheckBox
|
||||
editName.addValidator(UserListNameValidator(getString(R.string.invalid_list_name)))
|
||||
if (savedInstanceState == null) {
|
||||
editName.setText(arguments.getString(EXTRA_LIST_NAME))
|
||||
editDescription.setText(arguments.getString(EXTRA_DESCRIPTION))
|
||||
editPublic.isChecked = arguments.getBoolean(EXTRA_IS_PUBLIC, true)
|
||||
}
|
||||
if (mDescription != null) {
|
||||
editDescription!!.setText(mDescription)
|
||||
}
|
||||
editPublic!!.isChecked = mIsPublic
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
outState!!.putParcelable(EXTRA_ACCOUNT_KEY, mAccountKey)
|
||||
outState.putString(EXTRA_LIST_ID, mListId)
|
||||
outState.putString(EXTRA_LIST_NAME, mName)
|
||||
outState.putString(EXTRA_DESCRIPTION, mDescription)
|
||||
outState.putBoolean(EXTRA_IS_PUBLIC, mIsPublic)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ParcelableUserListLoader(
|
||||
|
@ -415,10 +391,10 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
|
|||
override fun loadInBackground(): SingleResponse<ParcelableUserList> {
|
||||
if (!omitIntentExtra && extras != null) {
|
||||
val cache = extras.getParcelable<ParcelableUserList>(EXTRA_USER_LIST)
|
||||
if (cache != null) return SingleResponse.Companion.getInstance(cache)
|
||||
if (cache != null) return SingleResponse(cache)
|
||||
}
|
||||
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey
|
||||
) ?: return SingleResponse.Companion.getInstance<ParcelableUserList>()
|
||||
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey)
|
||||
?: return SingleResponse(MicroBlogException("No account"))
|
||||
try {
|
||||
val list: UserList
|
||||
when {
|
||||
|
@ -432,12 +408,12 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
|
|||
list = twitter.showUserListByScrenName(listName, screenName)
|
||||
}
|
||||
else -> {
|
||||
return SingleResponse.Companion.getInstance<ParcelableUserList>()
|
||||
return SingleResponse(MicroBlogException("Invalid argument"))
|
||||
}
|
||||
}
|
||||
return SingleResponse.Companion.getInstance(ParcelableUserListUtils.from(list, accountKey))
|
||||
return SingleResponse(ParcelableUserListUtils.from(list, accountKey))
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.Companion.getInstance<ParcelableUserList>(e)
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
|||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.UserColorNameManager
|
||||
import org.mariotaku.twidere.util.Utils.findStatus
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||
import org.mariotaku.twidere.util.deleteActivityStatus
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ class ParcelableStatusLoader(
|
|||
}
|
||||
if (details == null) return SingleResponse(MicroBlogException("No account"))
|
||||
try {
|
||||
val status = findStatus(context, accountKey, statusId)
|
||||
val status = DataStoreUtils.findStatus(context, accountKey, statusId)
|
||||
ParcelableStatusUtils.updateExtraInformation(status, details)
|
||||
val response = SingleResponse(status)
|
||||
response.extras[EXTRA_ACCOUNT] = details
|
||||
|
@ -85,7 +85,7 @@ class ParcelableStatusLoader(
|
|||
// Delete all deleted status
|
||||
val cr = context.contentResolver
|
||||
DataStoreUtils.deleteStatus(cr, accountKey, statusId, null)
|
||||
DataStoreUtils.deleteActivityStatus(cr, accountKey, statusId, null)
|
||||
deleteActivityStatus(cr, accountKey, statusId, null)
|
||||
}
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
|
|
@ -7,15 +7,6 @@ abstract class SimpleRefreshTaskParam : RefreshTaskParam {
|
|||
|
||||
internal var cached: Array<UserKey>? = null
|
||||
|
||||
override val accountKeys: Array<UserKey>
|
||||
get() {
|
||||
if (cached != null) return cached!!
|
||||
cached = getAccountKeysWorker()
|
||||
return cached!!
|
||||
}
|
||||
|
||||
abstract fun getAccountKeysWorker(): Array<UserKey>
|
||||
|
||||
override val maxIds: Array<String?>?
|
||||
get() = null
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants.TIMELINE_POSITIONS_PREFERENCES_NAME
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
|
||||
class ClearDatabasesPreference(context: Context, attrs: AttributeSet? = null) : AsyncTaskPreference(context, attrs, R.attr.preferenceStyle) {
|
||||
|
||||
override fun doInBackground() {
|
||||
val context = context ?: return
|
||||
val resolver = context.contentResolver
|
||||
for (uri in DataStoreUtils.STATUSES_URIS) {
|
||||
if (CachedStatuses.CONTENT_URI == uri) {
|
||||
continue
|
||||
}
|
||||
resolver.delete(uri, null, null)
|
||||
}
|
||||
for (uri in DataStoreUtils.MESSAGES_URIS) {
|
||||
resolver.delete(uri, null, null)
|
||||
}
|
||||
for (uri in DataStoreUtils.CACHE_URIS) {
|
||||
resolver.delete(uri, null, null)
|
||||
}
|
||||
resolver.delete(Activities.AboutMe.CONTENT_URI, null, null)
|
||||
resolver.delete(Activities.ByFriends.CONTENT_URI, null, null)
|
||||
resolver.delete(Notifications.CONTENT_URI, null, null)
|
||||
resolver.delete(UnreadCounts.CONTENT_URI, null, null)
|
||||
resolver.delete(SavedSearches.CONTENT_URI, null, null)
|
||||
|
||||
val prefs = context.getSharedPreferences(TIMELINE_POSITIONS_PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
editor.clear()
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,6 @@ import org.mariotaku.microblog.library.twitter.model.User
|
|||
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableUser
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.FriendshipTaskEvent
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
|
@ -20,7 +19,8 @@ import org.mariotaku.twidere.model.util.ParcelableUserUtils
|
|||
abstract class AbsFriendshipOperationTask(
|
||||
context: Context,
|
||||
@FriendshipTaskEvent.Action protected val action: Int
|
||||
) : BaseAbstractTask<AbsFriendshipOperationTask.Arguments, SingleResponse<ParcelableUser>, Any?>(context) {
|
||||
) : ExceptionHandlingAbstractTask<AbsFriendshipOperationTask.Arguments, ParcelableUser,
|
||||
MicroBlogException, Any?>(context) {
|
||||
|
||||
override fun beforeExecute() {
|
||||
microBlogWrapper.addUpdatingRelationshipId(params.accountKey, params.userKey)
|
||||
|
@ -30,34 +30,31 @@ abstract class AbsFriendshipOperationTask(
|
|||
bus.post(event)
|
||||
}
|
||||
|
||||
override fun afterExecute(handler: Any?, result: SingleResponse<ParcelableUser>?) {
|
||||
override fun afterExecute(callback: Any?, result: ParcelableUser?, exception: MicroBlogException?) {
|
||||
microBlogWrapper.removeUpdatingRelationshipId(params.accountKey, params.userKey)
|
||||
val event = FriendshipTaskEvent(action, params.accountKey,
|
||||
params.userKey)
|
||||
event.isFinished = true
|
||||
if (result!!.hasData()) {
|
||||
val user = result.data!!
|
||||
if (result != null) {
|
||||
val user = result
|
||||
showSucceededMessage(params, user)
|
||||
event.isSucceeded = true
|
||||
event.user = result.data
|
||||
event.user = user
|
||||
} else {
|
||||
showErrorMessage(params, result.exception)
|
||||
showErrorMessage(params, exception)
|
||||
}
|
||||
bus.post(event)
|
||||
}
|
||||
|
||||
override fun doLongOperation(args: Arguments): SingleResponse<ParcelableUser> {
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(context), args.accountKey, true) ?: return SingleResponse.getInstance()
|
||||
override fun onExecute(params: Arguments): ParcelableUser {
|
||||
val am = AccountManager.get(context)
|
||||
val details = AccountUtils.getAccountDetails(am, params.accountKey, true)
|
||||
?: throw MicroBlogException("No account")
|
||||
val twitter = details.newMicroBlogInstance(context, cls = MicroBlog::class.java)
|
||||
try {
|
||||
val user = perform(twitter, details, args)
|
||||
val parcelableUser = ParcelableUserUtils.fromUser(user, args.accountKey)
|
||||
succeededWorker(twitter, details, args, parcelableUser)
|
||||
return SingleResponse.getInstance(parcelableUser)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUser>(e)
|
||||
}
|
||||
|
||||
val user = perform(twitter, details, params)
|
||||
val parcelableUser = ParcelableUserUtils.fromUser(user, params.accountKey)
|
||||
succeededWorker(twitter, details, params, parcelableUser)
|
||||
return parcelableUser
|
||||
}
|
||||
|
||||
@Throws(MicroBlogException::class)
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.widget.Toast
|
|||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.model.User
|
||||
import org.mariotaku.twidere.Constants
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
|
@ -17,7 +16,7 @@ import org.mariotaku.twidere.util.Utils
|
|||
/**
|
||||
* Created by mariotaku on 16/3/11.
|
||||
*/
|
||||
class AcceptFriendshipTask(context: Context) : AbsFriendshipOperationTask(context, FriendshipTaskEvent.Action.ACCEPT), Constants {
|
||||
class AcceptFriendshipTask(context: Context) : AbsFriendshipOperationTask(context, FriendshipTaskEvent.Action.ACCEPT) {
|
||||
|
||||
@Throws(MicroBlogException::class)
|
||||
override fun perform(twitter: MicroBlog, details: AccountDetails, args: AbsFriendshipOperationTask.Arguments): User {
|
||||
|
|
|
@ -20,23 +20,24 @@ class AddUserListMembersTask(
|
|||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val listId: String,
|
||||
private val users: Array<ParcelableUser>
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
private val users: Array<out ParcelableUser>
|
||||
) : BaseAbstractTask<Any?, SingleResponse<ParcelableUserList>, Any?>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?: return SingleResponse.getInstance<ParcelableUserList>()
|
||||
override fun doLongOperation(params: Any?): SingleResponse<ParcelableUserList> {
|
||||
try {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?:
|
||||
throw MicroBlogException("No account")
|
||||
val userIds = users.map(ParcelableUser::key).toTypedArray()
|
||||
val result = microBlog.addUserListMembers(listId, UserKey.getIds(userIds))
|
||||
val list = ParcelableUserListUtils.from(result, accountKey)
|
||||
return SingleResponse.getInstance(list)
|
||||
return SingleResponse(list)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUserList>(e)
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableUserList>) {
|
||||
override fun afterExecute(callback: Any?, result: SingleResponse<ParcelableUserList>) {
|
||||
if (result.data != null) {
|
||||
val message: String
|
||||
if (users.size == 1) {
|
||||
|
@ -56,7 +57,6 @@ class AddUserListMembersTask(
|
|||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_adding_member, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import java.util.*
|
|||
|
||||
class CacheUsersStatusesTask(
|
||||
private val context: Context
|
||||
) : AbstractTask<TwitterListResponse<Status>, Unit, Unit>() {
|
||||
) : AbstractTask<TwitterListResponse<Status>, Unit?, Unit?>() {
|
||||
|
||||
override fun doLongOperation(params: TwitterListResponse<Status>) {
|
||||
val resolver = context.contentResolver
|
||||
|
|
|
@ -25,10 +25,11 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
|||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper.calculateHashCode
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper.Companion.calculateHashCode
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.updateActivityStatus
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/7.
|
||||
|
@ -37,7 +38,7 @@ class CreateFavoriteTask(
|
|||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val status: ParcelableStatus
|
||||
) : BaseAbstractTask<Any, SingleResponse<ParcelableStatus>, Any>(context) {
|
||||
) : BaseAbstractTask<Any?, SingleResponse<ParcelableStatus>, Any?>(context) {
|
||||
|
||||
private val statusId = status.id
|
||||
|
||||
|
@ -82,7 +83,7 @@ class CreateFavoriteTask(
|
|||
for (uri in DataStoreUtils.STATUSES_URIS) {
|
||||
resolver.update(uri, values, statusWhere, statusWhereArgs)
|
||||
}
|
||||
DataStoreUtils.updateActivityStatus(resolver, accountKey, statusId) { activity ->
|
||||
updateActivityStatus(resolver, accountKey, statusId) { activity ->
|
||||
val statusesMatrix = arrayOf(activity.target_statuses, activity.target_object_statuses)
|
||||
for (statusesArray in statusesMatrix) {
|
||||
if (statusesArray == null) continue
|
||||
|
|
|
@ -22,9 +22,10 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
|||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper.calculateHashCode
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper.Companion.calculateHashCode
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.updateActivityStatus
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/7.
|
||||
|
@ -33,10 +34,8 @@ class DestroyFavoriteTask(
|
|||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val statusId: String
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableStatus>>(context) {
|
||||
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableStatus> {
|
||||
) : BaseAbstractTask<Any?, SingleResponse<ParcelableStatus>, Any?>(context) {
|
||||
override fun doLongOperation(params: Any?): SingleResponse<ParcelableStatus> {
|
||||
val resolver = context.contentResolver
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true)
|
||||
?: return SingleResponse.getInstance<ParcelableStatus>()
|
||||
|
@ -67,7 +66,7 @@ class DestroyFavoriteTask(
|
|||
resolver.update(uri, values, where.sql, whereArgs)
|
||||
}
|
||||
|
||||
DataStoreUtils.updateActivityStatus(resolver, accountKey, statusId, DataStoreUtils.UpdateActivityAction { activity ->
|
||||
updateActivityStatus(resolver, accountKey, statusId) { activity ->
|
||||
val statusesMatrix = arrayOf(activity.target_statuses, activity.target_object_statuses)
|
||||
for (statusesArray in statusesMatrix) {
|
||||
if (statusesArray == null) continue
|
||||
|
@ -79,7 +78,7 @@ class DestroyFavoriteTask(
|
|||
status.favorite_count = result.favorite_count - 1
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return SingleResponse.getInstance(result)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableStatus>(e)
|
||||
|
@ -87,8 +86,7 @@ class DestroyFavoriteTask(
|
|||
|
||||
}
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
override fun beforeExecute() {
|
||||
val hashCode = AsyncTwitterWrapper.calculateHashCode(accountKey, statusId)
|
||||
if (!destroyingFavoriteIds.contains(hashCode)) {
|
||||
destroyingFavoriteIds.add(hashCode)
|
||||
|
@ -96,7 +94,7 @@ class DestroyFavoriteTask(
|
|||
bus.post(StatusListChangedEvent())
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableStatus>) {
|
||||
override fun afterExecute(callback: Any?, result: SingleResponse<ParcelableStatus>) {
|
||||
destroyingFavoriteIds.removeElement(AsyncTwitterWrapper.calculateHashCode(accountKey, statusId))
|
||||
val taskEvent = FavoriteTaskEvent(FavoriteTaskEvent.Action.DESTROY,
|
||||
accountKey, statusId)
|
||||
|
@ -117,7 +115,6 @@ class DestroyFavoriteTask(
|
|||
}
|
||||
bus.post(taskEvent)
|
||||
bus.post(StatusListChangedEvent())
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
|||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.deleteActivityStatus
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/9.
|
||||
|
@ -25,9 +26,9 @@ class DestroyStatusTask(
|
|||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val statusId: String
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableStatus>>(context) {
|
||||
) : BaseAbstractTask<Any?, SingleResponse<ParcelableStatus>, Any?>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableStatus> {
|
||||
override fun doLongOperation(params: Any?): SingleResponse<ParcelableStatus> {
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true)
|
||||
?: return SingleResponse()
|
||||
val microBlog = details.newMicroBlogInstance(context, cls = MicroBlog::class.java)
|
||||
|
@ -45,22 +46,22 @@ class DestroyStatusTask(
|
|||
} finally {
|
||||
if (deleteStatus) {
|
||||
DataStoreUtils.deleteStatus(context.contentResolver, accountKey, statusId, status)
|
||||
DataStoreUtils.deleteActivityStatus(context.contentResolver, accountKey, statusId, status)
|
||||
deleteActivityStatus(context.contentResolver, accountKey, statusId, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
override fun beforeExecute() {
|
||||
val hashCode = AsyncTwitterWrapper.calculateHashCode(accountKey, statusId)
|
||||
if (!asyncTwitterWrapper.destroyingStatusIds.contains(hashCode)) {
|
||||
asyncTwitterWrapper.destroyingStatusIds.add(hashCode)
|
||||
if (!microBlogWrapper.destroyingStatusIds.contains(hashCode)) {
|
||||
microBlogWrapper.destroyingStatusIds.add(hashCode)
|
||||
}
|
||||
bus.post(StatusListChangedEvent())
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableStatus>) {
|
||||
asyncTwitterWrapper.destroyingStatusIds.removeElement(AsyncTwitterWrapper.calculateHashCode(accountKey, statusId))
|
||||
override fun afterExecute(callback: Any?, result: SingleResponse<ParcelableStatus>) {
|
||||
|
||||
microBlogWrapper.destroyingStatusIds.removeElement(AsyncTwitterWrapper.calculateHashCode(accountKey, statusId))
|
||||
if (result.hasData()) {
|
||||
val status = result.data!!
|
||||
if (status.retweet_id != null) {
|
||||
|
@ -72,7 +73,6 @@ class DestroyStatusTask(
|
|||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.UserListDestroyedEvent
|
||||
import org.mariotaku.twidere.model.util.ParcelableUserListUtils
|
||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/10.
|
||||
*/
|
||||
class DestroyUserListTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val listId: String
|
||||
) : BaseAbstractTask<Any?, SingleResponse<ParcelableUserList>, Any?>(context) {
|
||||
|
||||
override fun doLongOperation(params: Any?): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?:
|
||||
return SingleResponse(MicroBlogException("No account"))
|
||||
try {
|
||||
val userList = microBlog.destroyUserList(listId)
|
||||
val list = ParcelableUserListUtils.from(userList, accountKey)
|
||||
return SingleResponse(list)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: Any?, result: SingleResponse<ParcelableUserList>) {
|
||||
val context = context
|
||||
if (result.data != null) {
|
||||
val message = context.getString(R.string.deleted_list, result.data.name)
|
||||
Utils.showInfoMessage(context, message, false)
|
||||
bus.post(UserListDestroyedEvent(result.data))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/10.
|
||||
*/
|
||||
|
||||
abstract class ExceptionHandlingAbstractTask<Params, Result, in TaskException : Exception, Callback>(
|
||||
context: Context
|
||||
) : BaseAbstractTask<Params, SingleResponse<Result>, Callback>(context) {
|
||||
|
||||
override final fun afterExecute(callback: Callback?, result: SingleResponse<Result>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
afterExecute(callback, result.data, result.exception as? TaskException)
|
||||
if (result.data != null) {
|
||||
onSucceed(callback, result.data)
|
||||
} else if (result.exception != null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
onException(callback, result.exception as TaskException)
|
||||
}
|
||||
}
|
||||
|
||||
override final fun doLongOperation(params: Params): SingleResponse<Result> {
|
||||
try {
|
||||
return SingleResponse(onExecute(params))
|
||||
} catch (tr: TaskException) {
|
||||
return SingleResponse(tr)
|
||||
}
|
||||
}
|
||||
|
||||
open fun afterExecute(callback: Callback?, result: Result?, exception: TaskException?) {
|
||||
}
|
||||
|
||||
open fun onSucceed(callback: Callback?, result: Result) {
|
||||
}
|
||||
|
||||
open fun onException(callback: Callback?, exception: TaskException) {
|
||||
}
|
||||
|
||||
abstract fun onExecute(params: Params): Result
|
||||
}
|
|
@ -76,12 +76,31 @@ class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Uni
|
|||
private fun getDefaultMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshTaskParam, index: Int): GetMessagesData {
|
||||
val accountKey = details.key
|
||||
|
||||
|
||||
val sinceIds = if (param.hasSinceIds) param.sinceIds else null
|
||||
val maxIds = if (param.hasMaxIds) param.maxIds else null
|
||||
|
||||
val received = microBlog.getDirectMessages(Paging().apply {
|
||||
count(100)
|
||||
val maxId = maxIds?.get(index)
|
||||
val sinceId = sinceIds?.get(index)
|
||||
if (maxId != null) {
|
||||
maxId(maxId)
|
||||
}
|
||||
if (sinceIds != null) {
|
||||
sinceId(sinceId)
|
||||
}
|
||||
})
|
||||
val sent = microBlog.getSentDirectMessages(Paging().apply {
|
||||
count(100)
|
||||
val accountsCount = param.accountKeys.size
|
||||
val maxId = maxIds?.get(accountsCount + index)
|
||||
val sinceId = sinceIds?.get(accountsCount + index)
|
||||
if (maxId != null) {
|
||||
maxId(maxId)
|
||||
}
|
||||
if (sinceId != null) {
|
||||
sinceId(sinceId)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.BuildConfig
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
|
@ -38,6 +36,6 @@ class GetSavedSearchesTask(
|
|||
DebugLog.w(LOGTAG, tr = e)
|
||||
}
|
||||
}
|
||||
return SingleResponse.getInstance(Unit)
|
||||
return SingleResponse(Unit)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,7 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
|||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper.calculateHashCode
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.*
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/7.
|
||||
|
@ -72,7 +68,7 @@ class RetweetStatusTask(
|
|||
for (uri in DataStoreUtils.STATUSES_URIS) {
|
||||
resolver.update(uri, values, where.sql, whereArgs)
|
||||
}
|
||||
DataStoreUtils.updateActivityStatus(resolver, accountKey, statusId, DataStoreUtils.UpdateActivityAction { activity ->
|
||||
updateActivityStatus(resolver, accountKey, statusId) { activity ->
|
||||
val statusesMatrix = arrayOf(activity.target_statuses, activity.target_object_statuses)
|
||||
activity.status_my_retweet_id = result.my_retweet_id
|
||||
for (statusesArray in statusesMatrix) {
|
||||
|
@ -87,7 +83,7 @@ class RetweetStatusTask(
|
|||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
UpdateStatusTask.deleteDraft(context, draftId)
|
||||
return SingleResponse(result)
|
||||
} catch (e: MicroBlogException) {
|
||||
|
@ -127,7 +123,7 @@ class RetweetStatusTask(
|
|||
companion object {
|
||||
private val creatingRetweetIds = ArrayIntList()
|
||||
fun isCreatingRetweet(accountKey: UserKey?, statusId: String?): Boolean {
|
||||
return creatingRetweetIds.contains(calculateHashCode(accountKey, statusId))
|
||||
return creatingRetweetIds.contains(AsyncTwitterWrapper.calculateHashCode(accountKey, statusId))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import org.mariotaku.twidere.model.SingleResponse
|
|||
/**
|
||||
* Created by mariotaku on 2017/2/8.
|
||||
*/
|
||||
class SendMessageTask(context: Context) : BaseAbstractTask<Unit, SingleResponse<ParcelableMessage>, Unit>(context) {
|
||||
class SendMessageTask(
|
||||
context: Context
|
||||
) : BaseAbstractTask<Unit, SingleResponse<ParcelableMessage>, Unit>(context) {
|
||||
override fun doLongOperation(params: Unit?): SingleResponse<ParcelableMessage> {
|
||||
return SingleResponse(UnsupportedOperationException())
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@ package org.mariotaku.twidere.task
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.squareup.otto.Bus
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
|
@ -13,12 +10,11 @@ import org.mariotaku.twidere.model.SingleResponse
|
|||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.ProfileUpdatedEvent
|
||||
import org.mariotaku.twidere.model.util.ParcelableUserUtils
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||
import org.mariotaku.twidere.util.TwitterWrapper
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/3/11.
|
||||
|
@ -52,7 +48,7 @@ open class UpdateProfileBackgroundImageTask<ResultHandler>(
|
|||
try {
|
||||
Thread.sleep(5000L)
|
||||
} catch (e: InterruptedException) {
|
||||
Log.w(LOGTAG, e)
|
||||
DebugLog.w(LOGTAG, tr = e)
|
||||
}
|
||||
val user = twitter.verifyCredentials()
|
||||
return SingleResponse(ParcelableUserUtils.fromUser(user, accountKey))
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.model.UserListUpdate
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.UserListUpdatedEvent
|
||||
import org.mariotaku.twidere.model.util.ParcelableUserListUtils
|
||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/10.
|
||||
*/
|
||||
class UpdateUserListDetailsTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val listId: String,
|
||||
private val update: UserListUpdate
|
||||
) : BaseAbstractTask<Any, SingleResponse<ParcelableUserList>, Any>(context) {
|
||||
|
||||
override fun doLongOperation(o: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey)
|
||||
if (microBlog != null) {
|
||||
try {
|
||||
val list = microBlog.updateUserList(listId, update)
|
||||
return SingleResponse(ParcelableUserListUtils.from(list, accountKey))
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
return SingleResponse.getInstance<ParcelableUserList>()
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: Any?, result: SingleResponse<ParcelableUserList>) {
|
||||
if (result.data != null) {
|
||||
val message = context.getString(R.string.updated_list_details, result.data.name)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
bus.post(UserListUpdatedEvent(result.data))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_updating_details, result.exception, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -155,7 +155,7 @@ abstract class GetActivitiesTask(
|
|||
var olderCount = -1
|
||||
if (minPositionKey > 0) {
|
||||
olderCount = DataStoreUtils.getActivitiesCount(context, contentUri, minPositionKey,
|
||||
Activities.POSITION_KEY, false, details.key)
|
||||
Activities.POSITION_KEY, false, arrayOf(details.key))
|
||||
}
|
||||
val writeUri = UriUtils.appendQueryParameters(contentUri, QUERY_PARAM_NOTIFY, notify)
|
||||
if (deleteBound[0] > 0 && deleteBound[1] > 0) {
|
||||
|
|
|
@ -0,0 +1,780 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.support.v4.util.SimpleArrayMap
|
||||
import com.squareup.otto.Bus
|
||||
import com.squareup.otto.Subscribe
|
||||
import org.apache.commons.collections.primitives.ArrayIntList
|
||||
import org.apache.commons.collections.primitives.ArrayLongList
|
||||
import org.apache.commons.collections.primitives.IntList
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.http.HttpResponseCode
|
||||
import org.mariotaku.microblog.library.twitter.model.*
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants
|
||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.event.*
|
||||
import org.mariotaku.twidere.model.util.ParcelableUserListUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Inbox
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Outbox
|
||||
import org.mariotaku.twidere.task.*
|
||||
import org.mariotaku.twidere.util.collection.CompactHashSet
|
||||
import java.util.*
|
||||
|
||||
class AsyncTwitterWrapper(
|
||||
val context: Context,
|
||||
private val bus: Bus,
|
||||
private val preferences: SharedPreferencesWrapper,
|
||||
private val asyncTaskManager: AsyncTaskManager
|
||||
) {
|
||||
private val resolver = context.contentResolver
|
||||
|
||||
|
||||
var destroyingStatusIds: IntList = ArrayIntList()
|
||||
private val updatingRelationshipIds = ArrayIntList()
|
||||
|
||||
private val sendingDraftIds = ArrayLongList()
|
||||
|
||||
private val getMessageTasks = CompactHashSet<Uri>()
|
||||
private val getStatusTasks = CompactHashSet<Uri>()
|
||||
|
||||
init {
|
||||
bus.register(object : Any() {
|
||||
@Subscribe
|
||||
fun onGetDirectMessagesTaskEvent(event: GetMessagesTaskEvent) {
|
||||
if (event.running) {
|
||||
getMessageTasks.add(event.uri)
|
||||
} else {
|
||||
getMessageTasks.remove(event.uri)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onGetStatusesTaskEvent(event: GetStatusesTaskEvent) {
|
||||
if (event.running) {
|
||||
getStatusTasks.add(event.uri)
|
||||
} else {
|
||||
getStatusTasks.remove(event.uri)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun acceptFriendshipAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = AcceptFriendshipTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun addSendingDraftId(id: Long) {
|
||||
synchronized(sendingDraftIds) {
|
||||
sendingDraftIds.add(id)
|
||||
resolver.notifyChange(Drafts.CONTENT_URI_UNSENT, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun addUserListMembersAsync(accountKey: UserKey, listId: String, vararg users: ParcelableUser) {
|
||||
val task = AddUserListMembersTask(context, accountKey, listId, users)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun cancelRetweetAsync(accountKey: UserKey, statusId: String?, myRetweetId: String?) {
|
||||
if (myRetweetId != null) {
|
||||
destroyStatusAsync(accountKey, myRetweetId)
|
||||
} else if (statusId != null) {
|
||||
destroyStatusAsync(accountKey, statusId)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearNotificationAsync(notificationType: Int) {
|
||||
clearNotificationAsync(notificationType, null)
|
||||
}
|
||||
|
||||
fun clearNotificationAsync(notificationId: Int, accountKey: UserKey?) {
|
||||
val task = ClearNotificationTask(context, notificationId, accountKey)
|
||||
AsyncTaskUtils.executeTask(task)
|
||||
}
|
||||
|
||||
fun clearUnreadCountAsync(position: Int) {
|
||||
val task = ClearUnreadCountTask(position)
|
||||
AsyncTaskUtils.executeTask(task)
|
||||
}
|
||||
|
||||
fun createBlockAsync(accountKey: UserKey, userKey: UserKey, filterEverywhere: Boolean) {
|
||||
val task = CreateUserBlockTask(context, filterEverywhere)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun createFavoriteAsync(accountKey: UserKey, status: ParcelableStatus) {
|
||||
val task = CreateFavoriteTask(context, accountKey, status)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun createFriendshipAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = CreateFriendshipTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun createMultiBlockAsync(accountKey: UserKey, userIds: Array<String>): Int {
|
||||
val task = CreateMultiBlockTask(context, accountKey, userIds)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun createMuteAsync(accountKey: UserKey, userKey: UserKey, filterEverywhere: Boolean) {
|
||||
val task = CreateUserMuteTask(context, filterEverywhere)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun createSavedSearchAsync(accountKey: UserKey, query: String): Int {
|
||||
val task = CreateSavedSearchTask(accountKey, query)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun createUserListAsync(accountKey: UserKey, listName: String, isPublic: Boolean,
|
||||
description: String): Int {
|
||||
val task = CreateUserListTask(context, accountKey, listName, isPublic,
|
||||
description)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun createUserListSubscriptionAsync(accountKey: UserKey, listId: String): Int {
|
||||
val task = CreateUserListSubscriptionTask(accountKey, listId)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun deleteUserListMembersAsync(accountKey: UserKey, listId: String, vararg users: ParcelableUser): Int {
|
||||
val task = DeleteUserListMembersTask(accountKey, listId, users)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun denyFriendshipAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = DenyFriendshipTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyBlockAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = DestroyUserBlockTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyFavoriteAsync(accountKey: UserKey, statusId: String) {
|
||||
val task = DestroyFavoriteTask(context, accountKey, statusId)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyFriendshipAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = DestroyFriendshipTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyMuteAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = DestroyUserMuteTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroySavedSearchAsync(accountKey: UserKey, searchId: Long): Int {
|
||||
val task = DestroySavedSearchTask(context, accountKey, searchId)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun destroyStatusAsync(accountKey: UserKey, statusId: String) {
|
||||
val task = DestroyStatusTask(context, accountKey, statusId)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyUserListAsync(accountKey: UserKey, listId: String) {
|
||||
val task = DestroyUserListTask(context, accountKey, listId)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun destroyUserListSubscriptionAsync(accountKey: UserKey, listId: String): Int {
|
||||
val task = DestroyUserListSubscriptionTask(context, accountKey, listId)
|
||||
return asyncTaskManager.add(task, true)
|
||||
}
|
||||
|
||||
fun getHomeTimelineAsync(param: RefreshTaskParam): Boolean {
|
||||
val task = GetHomeTimelineTask(context)
|
||||
task.params = param
|
||||
TaskStarter.execute(task)
|
||||
return true
|
||||
}
|
||||
|
||||
fun getLocalTrendsAsync(accountId: UserKey, woeId: Int) {
|
||||
val task = GetTrendsTask(context, accountId, woeId)
|
||||
TaskStarter.execute<Any, Unit, Any>(task)
|
||||
}
|
||||
|
||||
fun getMessagesAsync(param: RefreshTaskParam) {
|
||||
val task = GetMessagesTask(context)
|
||||
task.params = param
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun getSavedSearchesAsync(accountKeys: Array<UserKey>) {
|
||||
val task = GetSavedSearchesTask(context)
|
||||
task.params = accountKeys
|
||||
TaskStarter.execute<Array<UserKey>, SingleResponse<Unit>, Any>(task)
|
||||
}
|
||||
|
||||
fun getSendingDraftIds(): LongArray {
|
||||
return sendingDraftIds.toArray()
|
||||
}
|
||||
|
||||
fun isDestroyingStatus(accountId: UserKey?, statusId: String?): Boolean {
|
||||
return destroyingStatusIds.contains(calculateHashCode(accountId, statusId))
|
||||
}
|
||||
|
||||
fun isStatusTimelineRefreshing(uri: Uri): Boolean {
|
||||
return getStatusTasks.contains(uri)
|
||||
}
|
||||
|
||||
fun refreshAll() {
|
||||
refreshAll { DataStoreUtils.getActivatedAccountKeys(context) }
|
||||
}
|
||||
|
||||
fun refreshAll(accountKeys: Array<UserKey>): Boolean {
|
||||
return refreshAll { accountKeys }
|
||||
}
|
||||
|
||||
fun refreshAll(action: () -> Array<UserKey>): Boolean {
|
||||
getHomeTimelineAsync(object : SimpleRefreshTaskParam() {
|
||||
|
||||
override val accountKeys: Array<UserKey> by lazy { action() }
|
||||
|
||||
override val sinceIds: Array<String?>? by lazy {
|
||||
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI,
|
||||
accountKeys.toNulls())
|
||||
}
|
||||
})
|
||||
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_MENTIONS)) {
|
||||
getActivitiesAboutMeAsync(object : SimpleRefreshTaskParam() {
|
||||
override val accountKeys: Array<UserKey> by lazy { action() }
|
||||
|
||||
override val sinceIds: Array<String?>? by lazy {
|
||||
DataStoreUtils.getNewestActivityMaxPositions(context,
|
||||
Activities.AboutMe.CONTENT_URI, accountKeys.toNulls())
|
||||
}
|
||||
})
|
||||
}
|
||||
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_DIRECT_MESSAGES)) {
|
||||
getMessagesAsync(object : SimpleRefreshTaskParam() {
|
||||
override val accountKeys: Array<UserKey> by lazy { action() }
|
||||
})
|
||||
}
|
||||
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_SAVED_SEARCHES)) {
|
||||
getSavedSearchesAsync(action())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun removeSendingDraftId(id: Long) {
|
||||
synchronized(sendingDraftIds) {
|
||||
sendingDraftIds.removeElement(id)
|
||||
resolver.notifyChange(Drafts.CONTENT_URI_UNSENT, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeUnreadCountsAsync(position: Int, counts: SimpleArrayMap<UserKey, Set<String>>) {
|
||||
val task = RemoveUnreadCountsTask(context, position, counts)
|
||||
AsyncTaskUtils.executeTask(task)
|
||||
}
|
||||
|
||||
fun reportMultiSpam(accountKey: UserKey, userIds: Array<String>) {
|
||||
// TODO implementation
|
||||
}
|
||||
|
||||
fun reportSpamAsync(accountKey: UserKey, userKey: UserKey) {
|
||||
val task = ReportSpamAndBlockTask(context)
|
||||
task.setup(accountKey, userKey)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun retweetStatusAsync(accountKey: UserKey, status: ParcelableStatus) {
|
||||
val task = RetweetStatusTask(context, accountKey, status)
|
||||
TaskStarter.execute<Any, SingleResponse<ParcelableStatus>, Any>(task)
|
||||
}
|
||||
|
||||
fun updateUserListDetails(accountKey: UserKey, listId: String, update: UserListUpdate) {
|
||||
val task = UpdateUserListDetailsTask(context, accountKey, listId, update)
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun updateFriendship(accountKey: UserKey, userKey: UserKey, update: FriendshipUpdate) {
|
||||
TaskStarter.execute(object : ExceptionHandlingAbstractTask<Any, Relationship, MicroBlogException, Any>(context) {
|
||||
override fun onExecute(params: Any): Relationship {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey)
|
||||
?: throw MicroBlogException("No account")
|
||||
val relationship = microBlog.updateFriendship(userKey.id, update)
|
||||
if (!relationship.isSourceWantRetweetsFromTarget) {
|
||||
// TODO remove cached retweets
|
||||
val where = Expression.and(
|
||||
Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Statuses.RETWEETED_BY_USER_KEY)
|
||||
)
|
||||
val selectionArgs = arrayOf(accountKey.toString(), userKey.toString())
|
||||
context.contentResolver.delete(Statuses.CONTENT_URI, where.sql, selectionArgs)
|
||||
}
|
||||
return relationship
|
||||
}
|
||||
|
||||
override fun onSucceed(callback: Any?, result: Relationship) {
|
||||
bus.post(FriendshipUpdatedEvent(accountKey, userKey, result))
|
||||
}
|
||||
|
||||
override fun onException(callback: Any?, exception: MicroBlogException) {
|
||||
DebugLog.w(TwidereConstants.LOGTAG, "Unable to update friendship", exception)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun getActivitiesAboutMeAsync(param: RefreshTaskParam) {
|
||||
val task = GetActivitiesAboutMeTask(context)
|
||||
task.params = param
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun setActivitiesAboutMeUnreadAsync(accountKeys: Array<UserKey>, cursor: Long) {
|
||||
val task = object : ExceptionHandlingAbstractTask<Any?, Unit, MicroBlogException, Any?>(context) {
|
||||
override fun onExecute(params: Any?) {
|
||||
for (accountId in accountKeys) {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountId) ?: continue
|
||||
if (!Utils.isOfficialCredentials(context, accountId)) continue
|
||||
microBlog.setActivitiesAboutMeUnread(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun addUpdatingRelationshipId(accountKey: UserKey, userId: UserKey) {
|
||||
updatingRelationshipIds.add(ParcelableUser.calculateHashCode(accountKey, userId))
|
||||
}
|
||||
|
||||
fun removeUpdatingRelationshipId(accountKey: UserKey, userId: UserKey) {
|
||||
updatingRelationshipIds.removeElement(ParcelableUser.calculateHashCode(accountKey, userId))
|
||||
}
|
||||
|
||||
fun isUpdatingRelationship(accountId: UserKey, userId: UserKey): Boolean {
|
||||
return updatingRelationshipIds.contains(ParcelableUser.calculateHashCode(accountId, userId))
|
||||
}
|
||||
|
||||
internal inner class ClearNotificationTask(
|
||||
private val context: Context,
|
||||
private val notificationType: Int,
|
||||
private val accountKey: UserKey?
|
||||
) : AsyncTask<Any, Any, Int>() {
|
||||
|
||||
override fun doInBackground(vararg params: Any): Int {
|
||||
return TwitterWrapper.clearNotification(context, notificationType, accountKey)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal inner class ClearUnreadCountTask(private val position: Int) : AsyncTask<Any, Any, Int>() {
|
||||
|
||||
override fun doInBackground(vararg params: Any): Int {
|
||||
return TwitterWrapper.clearUnreadCount(context, position)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal inner class CreateMultiBlockTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val userIds: Array<String>
|
||||
) : ManagedAsyncTask<Any, Any, ListResponse<String>>(context) {
|
||||
|
||||
private fun deleteCaches(list: List<String>) {
|
||||
// I bet you don't want to see these users in your auto complete list.
|
||||
//TODO insert to blocked users data
|
||||
val values = ContentValues()
|
||||
values.put(CachedRelationships.BLOCKING, true)
|
||||
values.put(CachedRelationships.FOLLOWING, false)
|
||||
values.put(CachedRelationships.FOLLOWED_BY, false)
|
||||
val where = Expression.inArgs(CachedRelationships.USER_KEY, list.size).sql
|
||||
val selectionArgs = list.toTypedArray()
|
||||
resolver.update(CachedRelationships.CONTENT_URI, values, where, selectionArgs)
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Any): ListResponse<String> {
|
||||
val blockedUsers = ArrayList<String>()
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey)
|
||||
if (microBlog != null) {
|
||||
for (userId in userIds) {
|
||||
try {
|
||||
val user = microBlog.createBlock(userId)
|
||||
blockedUsers.add(user.id)
|
||||
} catch (e: MicroBlogException) {
|
||||
deleteCaches(blockedUsers)
|
||||
return ListResponse.getListInstance<String>(e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
deleteCaches(blockedUsers)
|
||||
return ListResponse.getListInstance(blockedUsers)
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: ListResponse<String>) {
|
||||
if (result.hasData()) {
|
||||
Utils.showInfoMessage(context, R.string.users_blocked, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_blocking, result.exception, true)
|
||||
}
|
||||
bus.post(UsersBlockedEvent(accountKey, userIds))
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal inner class CreateSavedSearchTask(private val mAccountKey: UserKey, private val mQuery: String) : ManagedAsyncTask<Any, Any, SingleResponse<SavedSearch>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<SavedSearch>? {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey) ?: return null
|
||||
try {
|
||||
return SingleResponse.getInstance(microBlog.createSavedSearch(mQuery))
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<SavedSearch>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<SavedSearch>) {
|
||||
if (result.hasData()) {
|
||||
val message = context.getString(R.string.message_toast_search_name_saved, result.data!!.query)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
} else if (result.hasException()) {
|
||||
val exception = result.exception
|
||||
// https://github.com/TwidereProject/Twidere-Android/issues/244
|
||||
if (exception is MicroBlogException && exception.statusCode == 403) {
|
||||
val desc = context.getString(R.string.saved_searches_already_saved_hint)
|
||||
Utils.showErrorMessage(context, R.string.action_saving_search, desc, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_saving_search, exception, false)
|
||||
}
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal inner class CreateUserListSubscriptionTask(private val mAccountKey: UserKey, private val mListId: String) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey) ?: return SingleResponse.getInstance<ParcelableUserList>()
|
||||
try {
|
||||
val userList = microBlog.createUserListSubscription(mListId)
|
||||
val list = ParcelableUserListUtils.from(userList, mAccountKey)
|
||||
return SingleResponse.getInstance(list)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUserList>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableUserList>) {
|
||||
val succeed = result.hasData()
|
||||
if (succeed) {
|
||||
val message = context.getString(R.string.subscribed_to_list, result.data!!.name)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
bus.post(UserListSubscriptionEvent(UserListSubscriptionEvent.Action.SUBSCRIBE,
|
||||
result.data))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_subscribing_to_list, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class CreateUserListTask(context: Context, private val mAccountKey: UserKey, private val mListName: String?,
|
||||
private val mIsPublic: Boolean, private val mDescription: String) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey
|
||||
)
|
||||
if (microBlog == null || mListName == null)
|
||||
return SingleResponse.getInstance<ParcelableUserList>()
|
||||
try {
|
||||
val userListUpdate = UserListUpdate()
|
||||
userListUpdate.setName(mListName)
|
||||
userListUpdate.setMode(if (mIsPublic) UserList.Mode.PUBLIC else UserList.Mode.PRIVATE)
|
||||
userListUpdate.setDescription(mDescription)
|
||||
val list = microBlog.createUserList(userListUpdate)
|
||||
return SingleResponse.getInstance(ParcelableUserListUtils.from(list, mAccountKey))
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUserList>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableUserList>) {
|
||||
val context = context
|
||||
if (result.hasData()) {
|
||||
val userList = result.data
|
||||
val message = context.getString(R.string.created_list, userList!!.name)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
bus.post(UserListCreatedEvent(userList))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_creating_list, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal inner class DeleteUserListMembersTask(
|
||||
private val accountKey: UserKey,
|
||||
private val userListId: String,
|
||||
private val users: Array<out ParcelableUser>
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?: return SingleResponse.getInstance<ParcelableUserList>()
|
||||
try {
|
||||
val userIds = users.map { it.key }.toTypedArray()
|
||||
val userList = microBlog.deleteUserListMembers(userListId, UserKey.getIds(userIds))
|
||||
val list = ParcelableUserListUtils.from(userList, accountKey)
|
||||
return SingleResponse.getInstance(list)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUserList>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableUserList>) {
|
||||
val succeed = result.hasData()
|
||||
val message: String
|
||||
if (succeed) {
|
||||
if (users.size == 1) {
|
||||
val user = users[0]
|
||||
val nameFirst = preferences[nameFirstKey]
|
||||
val displayName = userColorNameManager.getDisplayName(user.key,
|
||||
user.name, user.screen_name, nameFirst)
|
||||
message = context.getString(R.string.deleted_user_from_list, displayName,
|
||||
result.data!!.name)
|
||||
} else {
|
||||
val res = context.resources
|
||||
message = res.getQuantityString(R.plurals.deleted_N_users_from_list, users.size, users.size,
|
||||
result.data!!.name)
|
||||
}
|
||||
bus.post(UserListMembersChangedEvent(UserListMembersChangedEvent.Action.REMOVED,
|
||||
result.data, users))
|
||||
Utils.showInfoMessage(context, message, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal inner class DestroyDirectMessageTask(private val mAccountKey: UserKey, private val mMessageId: String) : ManagedAsyncTask<Any, Any, SingleResponse<DirectMessage>>(context) {
|
||||
|
||||
private fun deleteMessages() {
|
||||
val where = Expression.and(Expression.equalsArgs(DirectMessages.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(DirectMessages.MESSAGE_ID)).sql
|
||||
val whereArgs = arrayOf(mAccountKey.toString(), mMessageId)
|
||||
resolver.delete(DirectMessages.Inbox.CONTENT_URI, where, whereArgs)
|
||||
resolver.delete(DirectMessages.Outbox.CONTENT_URI, where, whereArgs)
|
||||
}
|
||||
|
||||
private fun isMessageNotFound(e: Exception?): Boolean {
|
||||
if (e !is MicroBlogException) return false
|
||||
return e.errorCode == ErrorInfo.PAGE_NOT_FOUND || e.statusCode == HttpResponseCode.NOT_FOUND
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg args: Any): SingleResponse<DirectMessage> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey) ?: return SingleResponse.getInstance<DirectMessage>()
|
||||
try {
|
||||
val message = microBlog.destroyDirectMessage(mMessageId)
|
||||
deleteMessages()
|
||||
return SingleResponse.getInstance(message)
|
||||
} catch (e: MicroBlogException) {
|
||||
if (isMessageNotFound(e)) {
|
||||
deleteMessages()
|
||||
}
|
||||
return SingleResponse.getInstance<DirectMessage>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<DirectMessage>) {
|
||||
super.onPostExecute(result)
|
||||
if (result.hasData() || isMessageNotFound(result.exception)) {
|
||||
Utils.showInfoMessage(context, R.string.message_direct_message_deleted, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal inner class DestroyMessageConversationTask(private val mAccountKey: UserKey, private val mUserId: String) : ManagedAsyncTask<Any, Any, SingleResponse<Boolean>>(context) {
|
||||
|
||||
private fun deleteMessages(accountKey: UserKey, userId: String) {
|
||||
val whereArgs = arrayOf(accountKey.toString(), userId)
|
||||
resolver.delete(DirectMessages.Inbox.CONTENT_URI, Expression.and(
|
||||
Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Inbox.SENDER_ID)
|
||||
).sql, whereArgs)
|
||||
resolver.delete(DirectMessages.Outbox.CONTENT_URI, Expression.and(
|
||||
Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Outbox.RECIPIENT_ID)
|
||||
).sql, whereArgs)
|
||||
}
|
||||
|
||||
private fun isMessageNotFound(e: Exception?): Boolean {
|
||||
if (e !is MicroBlogException) return false
|
||||
return e.errorCode == ErrorInfo.PAGE_NOT_FOUND || e.statusCode == HttpResponseCode.NOT_FOUND
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg args: Any): SingleResponse<Boolean> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey) ?: return SingleResponse(MicroBlogException("No account"))
|
||||
try {
|
||||
microBlog.destroyDirectMessagesConversation(mAccountKey.id, mUserId)
|
||||
deleteMessages(mAccountKey, mUserId)
|
||||
return SingleResponse(true)
|
||||
} catch (e: MicroBlogException) {
|
||||
if (isMessageNotFound(e)) {
|
||||
deleteMessages(mAccountKey, mUserId)
|
||||
}
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<Boolean>) {
|
||||
super.onPostExecute(result)
|
||||
if (result.hasData() || isMessageNotFound(result.exception)) {
|
||||
Utils.showInfoMessage(context, R.string.message_direct_message_deleted, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal inner class DestroySavedSearchTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val searchId: Long
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<SavedSearch>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<SavedSearch> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?: return SingleResponse.getInstance<SavedSearch>()
|
||||
try {
|
||||
return SingleResponse.getInstance(microBlog.destroySavedSearch(searchId))
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<SavedSearch>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<SavedSearch>) {
|
||||
if (result.hasData()) {
|
||||
val message = context.getString(R.string.message_toast_search_name_deleted, result.data!!.query)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
bus.post(SavedSearchDestroyedEvent(accountKey, searchId))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting_search, result.exception, false)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal inner class DestroyUserListSubscriptionTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val listId: String
|
||||
) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?: return SingleResponse.getInstance<ParcelableUserList>()
|
||||
try {
|
||||
val userList = microBlog.destroyUserListSubscription(listId)
|
||||
val list = ParcelableUserListUtils.from(userList, accountKey)
|
||||
return SingleResponse.getInstance(list)
|
||||
} catch (e: MicroBlogException) {
|
||||
return SingleResponse.getInstance<ParcelableUserList>(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<ParcelableUserList>) {
|
||||
val succeed = result.hasData()
|
||||
if (succeed) {
|
||||
val message = context.getString(R.string.unsubscribed_from_list, result.data!!.name)
|
||||
Utils.showOkMessage(context, message, false)
|
||||
bus.post(UserListSubscriptionEvent(UserListSubscriptionEvent.Action.UNSUBSCRIBE,
|
||||
result.data))
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_unsubscribing_from_list, result.exception, true)
|
||||
}
|
||||
super.onPostExecute(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
fun calculateHashCode(accountId: UserKey?, statusId: String?): Int {
|
||||
return (accountId?.hashCode() ?: 0) xor (statusId?.hashCode() ?: 0)
|
||||
}
|
||||
|
||||
fun <T : Response<*>> getException(responses: List<T>): Exception? {
|
||||
for (response in responses) {
|
||||
if (response.hasException()) return response.exception
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,18 +2,22 @@ package org.mariotaku.twidere.util
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.support.annotation.WorkerThread
|
||||
import android.support.v4.util.LongSparseArray
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
import org.mariotaku.sqliteqb.library.*
|
||||
import org.mariotaku.twidere.constant.filterPossibilitySensitiveStatusesKey
|
||||
import org.mariotaku.twidere.constant.filterUnavailableQuoteStatusesKey
|
||||
import org.mariotaku.twidere.model.DraftCursorIndices
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.ParcelableStatus.FilterFlags
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.util.DataStoreUtils.ACTIVITIES_URIS
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/24.
|
||||
|
@ -113,4 +117,101 @@ fun deleteAccountData(resolver: ContentResolver, accountKey: UserKey) {
|
|||
resolver.delete(Activities.AboutMe.CONTENT_URI, where, whereArgs)
|
||||
resolver.delete(Messages.CONTENT_URI, where, whereArgs)
|
||||
resolver.delete(Messages.Conversations.CONTENT_URI, where, whereArgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun deleteActivityStatus(cr: ContentResolver, accountKey: UserKey,
|
||||
statusId: String, result: ParcelableStatus?) {
|
||||
|
||||
val host = accountKey.host
|
||||
val deleteWhere: String
|
||||
val updateWhere: String
|
||||
val deleteWhereArgs: Array<String>
|
||||
val updateWhereArgs: Array<String>
|
||||
if (host != null) {
|
||||
deleteWhere = Expression.and(
|
||||
Expression.likeRaw(Columns.Column(Activities.ACCOUNT_KEY), "'%@'||?"),
|
||||
Expression.or(
|
||||
Expression.equalsArgs(Activities.STATUS_ID),
|
||||
Expression.equalsArgs(Activities.STATUS_RETWEET_ID)
|
||||
)).sql
|
||||
deleteWhereArgs = arrayOf(host, statusId, statusId)
|
||||
updateWhere = Expression.and(
|
||||
Expression.likeRaw(Columns.Column(Activities.ACCOUNT_KEY), "'%@'||?"),
|
||||
Expression.equalsArgs(Activities.STATUS_MY_RETWEET_ID)
|
||||
).sql
|
||||
updateWhereArgs = arrayOf(host, statusId)
|
||||
} else {
|
||||
deleteWhere = Expression.or(
|
||||
Expression.equalsArgs(Activities.STATUS_ID),
|
||||
Expression.equalsArgs(Activities.STATUS_RETWEET_ID)
|
||||
).sql
|
||||
deleteWhereArgs = arrayOf(statusId, statusId)
|
||||
updateWhere = Expression.equalsArgs(Activities.STATUS_MY_RETWEET_ID).sql
|
||||
updateWhereArgs = arrayOf(statusId)
|
||||
}
|
||||
for (uri in ACTIVITIES_URIS) {
|
||||
cr.delete(uri, deleteWhere, deleteWhereArgs)
|
||||
updateActivity(cr, uri, updateWhere, updateWhereArgs) { activity ->
|
||||
activity.status_my_retweet_id = null
|
||||
arrayOf(activity.target_statuses, activity.target_object_statuses).filterNotNull().forEach {
|
||||
for (status in it) {
|
||||
if (statusId == status.id || statusId == status.retweet_id || statusId == status.my_retweet_id) {
|
||||
status.my_retweet_id = null
|
||||
if (result != null) {
|
||||
status.reply_count = result.reply_count
|
||||
status.retweet_count = result.retweet_count - 1
|
||||
status.favorite_count = result.favorite_count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateActivityStatus(resolver: ContentResolver,
|
||||
accountKey: UserKey,
|
||||
statusId: String,
|
||||
action: (ParcelableActivity) -> Unit) {
|
||||
val activityWhere = Expression.and(
|
||||
Expression.equalsArgs(Activities.ACCOUNT_KEY),
|
||||
Expression.or(
|
||||
Expression.equalsArgs(Activities.STATUS_ID),
|
||||
Expression.equalsArgs(Activities.STATUS_RETWEET_ID)
|
||||
)
|
||||
).sql
|
||||
val activityWhereArgs = arrayOf(accountKey.toString(), statusId, statusId)
|
||||
for (uri in ACTIVITIES_URIS) {
|
||||
updateActivity(resolver, uri, activityWhere, activityWhereArgs, action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@WorkerThread
|
||||
fun updateActivity(cr: ContentResolver, uri: Uri,
|
||||
where: String?, whereArgs: Array<String>?,
|
||||
action: (ParcelableActivity) -> Unit) {
|
||||
val c = cr.query(uri, Activities.COLUMNS, where, whereArgs, null) ?: return
|
||||
val values = LongSparseArray<ContentValues>()
|
||||
try {
|
||||
val ci = ParcelableActivityCursorIndices(c)
|
||||
c.moveToFirst()
|
||||
while (!c.isAfterLast) {
|
||||
val activity = ci.newObject(c)
|
||||
action(activity)
|
||||
values.put(activity._id, ParcelableActivityValuesCreator.create(activity))
|
||||
c.moveToNext()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
return
|
||||
} finally {
|
||||
c.close()
|
||||
}
|
||||
val updateWhere = Expression.equalsArgs(Activities._ID).sql
|
||||
val updateWhereArgs = arrayOfNulls<String>(1)
|
||||
for (i in 0 until values.size()) {
|
||||
updateWhereArgs[0] = values.keyAt(i).toString()
|
||||
cr.update(uri, values.valueAt(i), updateWhere, updateWhereArgs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,886 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.util
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.*
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.provider.BaseColumns
|
||||
import android.support.annotation.WorkerThread
|
||||
import android.text.TextUtils
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.sqliteqb.library.*
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column
|
||||
import org.mariotaku.sqliteqb.library.query.SQLSelectQuery
|
||||
import org.mariotaku.twidere.TwidereConstants.*
|
||||
import org.mariotaku.twidere.constant.IntentConstants
|
||||
import org.mariotaku.twidere.constant.databaseItemLimitKey
|
||||
import org.mariotaku.twidere.extension.model.getAccountKey
|
||||
import org.mariotaku.twidere.extension.model.getAccountUser
|
||||
import org.mariotaku.twidere.extension.model.getColor
|
||||
import org.mariotaku.twidere.extension.model.isActivated
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.tab.extra.HomeTabExtras
|
||||
import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras
|
||||
import org.mariotaku.twidere.model.tab.extra.TabExtras
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/11/28.
|
||||
*/
|
||||
object DataStoreUtils {
|
||||
|
||||
val STATUSES_URIS = arrayOf(Statuses.CONTENT_URI, CachedStatuses.CONTENT_URI)
|
||||
val CACHE_URIS = arrayOf(CachedUsers.CONTENT_URI, CachedStatuses.CONTENT_URI, CachedHashtags.CONTENT_URI, CachedTrends.Local.CONTENT_URI)
|
||||
val MESSAGES_URIS = arrayOf(Messages.CONTENT_URI, Messages.Conversations.CONTENT_URI)
|
||||
val ACTIVITIES_URIS = arrayOf(Activities.AboutMe.CONTENT_URI)
|
||||
|
||||
private val CONTENT_PROVIDER_URI_MATCHER = UriMatcher(UriMatcher.NO_MATCH)
|
||||
|
||||
init {
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Statuses.CONTENT_PATH,
|
||||
TABLE_ID_STATUSES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Activities.AboutMe.CONTENT_PATH,
|
||||
TABLE_ID_ACTIVITIES_ABOUT_ME)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Activities.ByFriends.CONTENT_PATH,
|
||||
TABLE_ID_ACTIVITIES_BY_FRIENDS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH,
|
||||
TABLE_ID_DRAFTS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedUsers.CONTENT_PATH,
|
||||
TABLE_ID_CACHED_USERS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Filters.Users.CONTENT_PATH,
|
||||
TABLE_ID_FILTERED_USERS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Filters.Keywords.CONTENT_PATH,
|
||||
TABLE_ID_FILTERED_KEYWORDS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Filters.Sources.CONTENT_PATH,
|
||||
TABLE_ID_FILTERED_SOURCES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Filters.Links.CONTENT_PATH,
|
||||
TABLE_ID_FILTERED_LINKS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Filters.Subscriptions.CONTENT_PATH,
|
||||
TABLE_ID_FILTERS_SUBSCRIPTIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Messages.CONTENT_PATH,
|
||||
TABLE_ID_MESSAGES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Messages.Conversations.CONTENT_PATH,
|
||||
TABLE_ID_MESSAGES_CONVERSATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedTrends.Local.CONTENT_PATH,
|
||||
TABLE_ID_TRENDS_LOCAL)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Tabs.CONTENT_PATH,
|
||||
TABLE_ID_TABS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedStatuses.CONTENT_PATH,
|
||||
TABLE_ID_CACHED_STATUSES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedHashtags.CONTENT_PATH,
|
||||
TABLE_ID_CACHED_HASHTAGS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedRelationships.CONTENT_PATH,
|
||||
TABLE_ID_CACHED_RELATIONSHIPS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, SavedSearches.CONTENT_PATH,
|
||||
TABLE_ID_SAVED_SEARCHES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, SearchHistory.CONTENT_PATH,
|
||||
TABLE_ID_SEARCH_HISTORY)
|
||||
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Notifications.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_NOTIFICATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Notifications.CONTENT_PATH + "/#",
|
||||
VIRTUAL_TABLE_ID_NOTIFICATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Notifications.CONTENT_PATH + "/#/*",
|
||||
VIRTUAL_TABLE_ID_NOTIFICATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Permissions.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_PERMISSIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, DNS.CONTENT_PATH + "/*",
|
||||
VIRTUAL_TABLE_ID_DNS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedImages.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_CACHED_IMAGES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CacheFiles.CONTENT_PATH + "/*",
|
||||
VIRTUAL_TABLE_ID_CACHE_FILES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Preferences.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_ALL_PREFERENCES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Preferences.CONTENT_PATH + "/*",
|
||||
VIRTUAL_TABLE_ID_PREFERENCES)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, UnreadCounts.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_UNREAD_COUNTS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, UnreadCounts.CONTENT_PATH + "/#",
|
||||
VIRTUAL_TABLE_ID_UNREAD_COUNTS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, UnreadCounts.CONTENT_PATH + "/#/#/*",
|
||||
VIRTUAL_TABLE_ID_UNREAD_COUNTS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, UnreadCounts.ByType.CONTENT_PATH + "/*",
|
||||
VIRTUAL_TABLE_ID_UNREAD_COUNTS_BY_TYPE)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedUsers.CONTENT_PATH_WITH_RELATIONSHIP + "/*",
|
||||
VIRTUAL_TABLE_ID_CACHED_USERS_WITH_RELATIONSHIP)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, CachedUsers.CONTENT_PATH_WITH_SCORE + "/*",
|
||||
VIRTUAL_TABLE_ID_CACHED_USERS_WITH_SCORE)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_UNSENT,
|
||||
VIRTUAL_TABLE_ID_DRAFTS_UNSENT)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_NOTIFICATIONS,
|
||||
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_NOTIFICATIONS,
|
||||
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Suggestions.AutoComplete.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_SUGGESTIONS_AUTO_COMPLETE)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Suggestions.Search.CONTENT_PATH,
|
||||
VIRTUAL_TABLE_ID_SUGGESTIONS_SEARCH)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, TwidereDataStore.CONTENT_PATH_DATABASE_PREPARE,
|
||||
VIRTUAL_TABLE_ID_DATABASE_PREPARE)
|
||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, TwidereDataStore.CONTENT_PATH_NULL,
|
||||
VIRTUAL_TABLE_ID_NULL)
|
||||
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)
|
||||
}
|
||||
|
||||
fun getNewestStatusIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
|
||||
return getStringFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.STATUS_ID,
|
||||
OrderBy(SQLFunctions.MAX(Statuses.STATUS_TIMESTAMP)), null, null)
|
||||
}
|
||||
|
||||
fun getMessageIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>, outgoing: Boolean): Array<String?> {
|
||||
val having: Expression
|
||||
if (outgoing) {
|
||||
having = Expression.equals(Messages.IS_OUTGOING, 1)
|
||||
} else {
|
||||
having = Expression.notEquals(Messages.IS_OUTGOING, 1)
|
||||
}
|
||||
return getStringFieldArray(context, uri, accountKeys, Messages.ACCOUNT_KEY, Messages.MESSAGE_ID,
|
||||
OrderBy(SQLFunctions.MAX(Messages.LOCAL_TIMESTAMP)), having, null)
|
||||
}
|
||||
|
||||
|
||||
fun getNewestStatusSortIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
|
||||
return getLongFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.SORT_ID,
|
||||
OrderBy(SQLFunctions.MAX(Statuses.STATUS_TIMESTAMP)), null, null)
|
||||
}
|
||||
|
||||
|
||||
fun getOldestStatusIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
|
||||
return getStringFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.STATUS_ID,
|
||||
OrderBy(SQLFunctions.MIN(Statuses.STATUS_TIMESTAMP)), null, null)
|
||||
}
|
||||
|
||||
|
||||
fun getOldestStatusSortIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
|
||||
return getLongFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY,
|
||||
Statuses.SORT_ID, OrderBy(SQLFunctions.MIN(Statuses.STATUS_TIMESTAMP)), null,
|
||||
null)
|
||||
}
|
||||
|
||||
fun getNewestActivityMaxPositions(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
|
||||
return getStringFieldArray(context, uri, accountKeys, Activities.ACCOUNT_KEY,
|
||||
Activities.MAX_REQUEST_POSITION, OrderBy(SQLFunctions.MAX(Activities.TIMESTAMP)),
|
||||
null, null)
|
||||
}
|
||||
|
||||
fun getOldestActivityMaxPositions(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
|
||||
return getStringFieldArray(context, uri, accountKeys, Activities.ACCOUNT_KEY,
|
||||
Activities.MAX_REQUEST_POSITION, OrderBy(SQLFunctions.MIN(Activities.TIMESTAMP)),
|
||||
null, null)
|
||||
}
|
||||
|
||||
fun getNewestActivityMaxSortPositions(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
|
||||
return getLongFieldArray(context, uri, accountKeys, Activities.ACCOUNT_KEY,
|
||||
Activities.MAX_SORT_POSITION, OrderBy(SQLFunctions.MAX(Activities.TIMESTAMP)),
|
||||
null, null)
|
||||
}
|
||||
|
||||
fun getOldestActivityMaxSortPositions(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
|
||||
return getLongFieldArray(context, uri, accountKeys, Activities.ACCOUNT_KEY,
|
||||
Activities.MAX_SORT_POSITION, OrderBy(SQLFunctions.MIN(Activities.TIMESTAMP)),
|
||||
null, null)
|
||||
}
|
||||
|
||||
fun getStatusCount(context: Context, uri: Uri, accountId: UserKey): Int {
|
||||
val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql
|
||||
val whereArgs = arrayOf(accountId.toString())
|
||||
return queryCount(context, uri, where, whereArgs)
|
||||
}
|
||||
|
||||
fun getActivitiesCount(context: Context, uri: Uri,
|
||||
accountKey: UserKey): Int {
|
||||
val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql
|
||||
return queryCount(context, uri, where, arrayOf(accountKey.toString()))
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
fun getFilteredUserIds(context: Context?): Array<UserKey> {
|
||||
if (context == null) return emptyArray()
|
||||
val resolver = context.contentResolver
|
||||
val projection = arrayOf(Filters.Users.USER_KEY)
|
||||
return resolver.query(Filters.Users.CONTENT_URI, projection, null, null, null)?.useCursor { cur ->
|
||||
return@useCursor Array(cur.count) { i ->
|
||||
cur.moveToPosition(i)
|
||||
UserKey.valueOf(cur.getString(0))
|
||||
}
|
||||
} ?: emptyArray()
|
||||
}
|
||||
|
||||
fun getAccountDisplayName(context: Context, accountKey: UserKey, nameFirst: Boolean): String? {
|
||||
val name: String?
|
||||
if (nameFirst) {
|
||||
name = getAccountName(context, accountKey)
|
||||
} else {
|
||||
name = "@${getAccountScreenName(context, accountKey)}"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fun getAccountName(context: Context, accountKey: UserKey): String? {
|
||||
val am = AccountManager.get(context)
|
||||
val account = AccountUtils.findByAccountKey(am, accountKey) ?: return null
|
||||
|
||||
return account.getAccountUser(am).name
|
||||
}
|
||||
|
||||
fun getAccountScreenName(context: Context, accountKey: UserKey): String? {
|
||||
val am = AccountManager.get(context)
|
||||
val account = AccountUtils.findByAccountKey(am, accountKey) ?: return null
|
||||
return account.getAccountUser(am).screen_name
|
||||
}
|
||||
|
||||
fun getActivatedAccountKeys(context: Context): Array<UserKey> {
|
||||
val am = AccountManager.get(context)
|
||||
val keys = ArrayList<UserKey>()
|
||||
for (account in AccountUtils.getAccounts(am)) {
|
||||
if (account.isActivated(am)) {
|
||||
keys.add(account.getAccountKey(am))
|
||||
}
|
||||
}
|
||||
return keys.toTypedArray()
|
||||
}
|
||||
|
||||
fun getStatusesCount(context: Context,
|
||||
preferences: SharedPreferences,
|
||||
uri: Uri,
|
||||
extraArgs: Bundle?, compare: Long,
|
||||
compareColumn: String, greaterThan: Boolean,
|
||||
accountKeys: Array<UserKey>?): Int {
|
||||
val keys = accountKeys ?: getActivatedAccountKeys(context)
|
||||
|
||||
val expressions = ArrayList<Expression>()
|
||||
val expressionArgs = ArrayList<String>()
|
||||
|
||||
expressions.add(Expression.inArgs(Column(Statuses.ACCOUNT_KEY), keys.size))
|
||||
for (accountKey in keys) {
|
||||
expressionArgs.add(accountKey.toString())
|
||||
}
|
||||
|
||||
if (greaterThan) {
|
||||
expressions.add(Expression.greaterThanArgs(compareColumn))
|
||||
} else {
|
||||
expressions.add(Expression.lesserThanArgs(compareColumn))
|
||||
}
|
||||
expressionArgs.add(compare.toString())
|
||||
|
||||
expressions.add(buildStatusFilterWhereClause(preferences, getTableNameByUri(uri)!!, null))
|
||||
|
||||
if (extraArgs != null) {
|
||||
val extras = extraArgs.getParcelable<Parcelable>(EXTRA_EXTRAS)
|
||||
if (extras is HomeTabExtras) {
|
||||
processTabExtras(expressions, expressionArgs, extras)
|
||||
}
|
||||
}
|
||||
|
||||
val selection = Expression.and(*expressions.toTypedArray())
|
||||
return queryCount(context, uri, selection.sql, expressionArgs.toTypedArray())
|
||||
}
|
||||
|
||||
fun getActivitiesCount(context: Context, uri: Uri, compare: Long,
|
||||
compareColumn: String, greaterThan: Boolean, accountKeys: Array<UserKey>?): Int {
|
||||
val keys = accountKeys ?: getActivatedAccountKeys(context)
|
||||
val selection = Expression.and(
|
||||
Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size),
|
||||
if (greaterThan) Expression.greaterThanArgs(compareColumn) else Expression.lesserThanArgs(compareColumn),
|
||||
buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null)
|
||||
)
|
||||
val whereArgs = arrayListOf<String>()
|
||||
keys.mapTo(whereArgs) { it.toString() }
|
||||
whereArgs.add(compare.toString())
|
||||
return queryCount(context, uri, selection.sql, whereArgs.toTypedArray())
|
||||
}
|
||||
|
||||
fun getActivitiesCount(context: Context, uri: Uri,
|
||||
extraWhere: Expression?, extraWhereArgs: Array<String>?,
|
||||
since: Long, sinceColumn: String, followingOnly: Boolean,
|
||||
accountKeys: Array<UserKey>?): Int {
|
||||
val keys = (accountKeys ?: getActivatedAccountKeys(context)).map { it.toString() }.toTypedArray()
|
||||
val expressions = ArrayList<Expression>()
|
||||
expressions.add(Expression.inArgs(Column(Activities.ACCOUNT_KEY), keys.size))
|
||||
expressions.add(Expression.greaterThanArgs(sinceColumn))
|
||||
expressions.add(buildActivityFilterWhereClause(getTableNameByUri(uri)!!, null))
|
||||
if (extraWhere != null) {
|
||||
expressions.add(extraWhere)
|
||||
}
|
||||
val selection = Expression.and(*expressions.toTypedArray())
|
||||
val selectionArgs: Array<String>
|
||||
if (extraWhereArgs != null) {
|
||||
selectionArgs = keys + since.toString() + extraWhereArgs
|
||||
} else {
|
||||
selectionArgs = keys + since.toString()
|
||||
}
|
||||
// If followingOnly option is on, we have to iterate over items
|
||||
if (followingOnly) {
|
||||
val resolver = context.contentResolver
|
||||
val projection = arrayOf(Activities.SOURCES)
|
||||
val cur = resolver.query(uri, projection, selection.sql, selectionArgs, null) ?: return -1
|
||||
try {
|
||||
val mapper = LoganSquare.mapperFor(UserFollowState::class.java)
|
||||
var total = 0
|
||||
cur.moveToFirst()
|
||||
while (!cur.isAfterLast) {
|
||||
val string = cur.getString(0)
|
||||
if (TextUtils.isEmpty(string)) continue
|
||||
var hasFollowing = false
|
||||
try {
|
||||
for (state in mapper.parseList(string)) {
|
||||
if (state.is_following) {
|
||||
hasFollowing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (hasFollowing) {
|
||||
total++
|
||||
}
|
||||
cur.moveToNext()
|
||||
}
|
||||
return total
|
||||
} finally {
|
||||
cur.close()
|
||||
}
|
||||
}
|
||||
return queryCount(context, uri, selection.sql, selectionArgs)
|
||||
}
|
||||
|
||||
fun getTableId(uri: Uri?): Int {
|
||||
if (uri == null) return -1
|
||||
return CONTENT_PROVIDER_URI_MATCHER.match(uri)
|
||||
}
|
||||
|
||||
fun getTableNameById(id: Int): String? {
|
||||
when (id) {
|
||||
TABLE_ID_STATUSES -> return Statuses.TABLE_NAME
|
||||
TABLE_ID_ACTIVITIES_ABOUT_ME -> return Activities.AboutMe.TABLE_NAME
|
||||
TABLE_ID_ACTIVITIES_BY_FRIENDS -> return Activities.ByFriends.TABLE_NAME
|
||||
TABLE_ID_DRAFTS -> return Drafts.TABLE_NAME
|
||||
TABLE_ID_FILTERED_USERS -> return Filters.Users.TABLE_NAME
|
||||
TABLE_ID_FILTERED_KEYWORDS -> return Filters.Keywords.TABLE_NAME
|
||||
TABLE_ID_FILTERED_SOURCES -> return Filters.Sources.TABLE_NAME
|
||||
TABLE_ID_FILTERED_LINKS -> return Filters.Links.TABLE_NAME
|
||||
TABLE_ID_FILTERS_SUBSCRIPTIONS -> return Filters.Subscriptions.TABLE_NAME
|
||||
TABLE_ID_MESSAGES -> return Messages.TABLE_NAME
|
||||
TABLE_ID_MESSAGES_CONVERSATIONS -> return Messages.Conversations.TABLE_NAME
|
||||
TABLE_ID_TRENDS_LOCAL -> return CachedTrends.Local.TABLE_NAME
|
||||
TABLE_ID_TABS -> return Tabs.TABLE_NAME
|
||||
TABLE_ID_CACHED_STATUSES -> return CachedStatuses.TABLE_NAME
|
||||
TABLE_ID_CACHED_USERS -> return CachedUsers.TABLE_NAME
|
||||
TABLE_ID_CACHED_HASHTAGS -> return CachedHashtags.TABLE_NAME
|
||||
TABLE_ID_CACHED_RELATIONSHIPS -> return CachedRelationships.TABLE_NAME
|
||||
TABLE_ID_SAVED_SEARCHES -> return SavedSearches.TABLE_NAME
|
||||
TABLE_ID_SEARCH_HISTORY -> return SearchHistory.TABLE_NAME
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
fun getTableNameByUri(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
return getTableNameById(getTableId(uri))
|
||||
}
|
||||
|
||||
fun buildActivityFilterWhereClause(table: String, extraSelection: Expression?): Expression {
|
||||
val filteredUsersQuery = SQLQueryBuilder
|
||||
.select(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.USER_KEY))
|
||||
.from(Tables(Filters.Users.TABLE_NAME))
|
||||
.build()
|
||||
val filteredUsersWhere = Expression.or(
|
||||
Expression.`in`(Column(Table(table), Activities.STATUS_USER_KEY), filteredUsersQuery),
|
||||
Expression.`in`(Column(Table(table), Activities.STATUS_RETWEETED_BY_USER_KEY), filteredUsersQuery),
|
||||
Expression.`in`(Column(Table(table), Activities.STATUS_QUOTED_USER_KEY), filteredUsersQuery)
|
||||
)
|
||||
val filteredIdsQueryBuilder = SQLQueryBuilder
|
||||
.select(Column(Table(table), Activities._ID))
|
||||
.from(Tables(table))
|
||||
.where(filteredUsersWhere)
|
||||
.union()
|
||||
.select(Columns(Column(Table(table), Activities._ID)))
|
||||
.from(Tables(table, Filters.Sources.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'"),
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_QUOTE_SOURCE),
|
||||
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'")
|
||||
))
|
||||
.union()
|
||||
.select(Columns(Column(Table(table), Activities._ID)))
|
||||
.from(Tables(table, Filters.Keywords.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'"),
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_QUOTE_TEXT_PLAIN),
|
||||
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'")
|
||||
))
|
||||
.union()
|
||||
.select(Columns(Column(Table(table), Activities._ID)))
|
||||
.from(Tables(table, Filters.Links.TABLE_NAME))
|
||||
.where(Expression.or(
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_SPANS),
|
||||
"'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'"),
|
||||
Expression.likeRaw(Column(Table(table), Activities.STATUS_QUOTE_SPANS),
|
||||
"'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'")
|
||||
))
|
||||
val filterExpression = Expression.or(
|
||||
Expression.notIn(Column(Table(table), Activities._ID), filteredIdsQueryBuilder.build()),
|
||||
Expression.equals(Column(Table(table), Activities.IS_GAP), 1)
|
||||
)
|
||||
if (extraSelection != null) {
|
||||
return Expression.and(filterExpression, extraSelection)
|
||||
}
|
||||
return filterExpression
|
||||
}
|
||||
|
||||
fun getAccountColors(context: Context, accountKeys: Array<UserKey>): IntArray {
|
||||
val am = AccountManager.get(context)
|
||||
val colors = IntArray(accountKeys.size)
|
||||
for (i in accountKeys.indices) {
|
||||
val account = AccountUtils.findByAccountKey(am, accountKeys[i])
|
||||
if (account != null) {
|
||||
colors[i] = account.getColor(am)
|
||||
}
|
||||
}
|
||||
return colors
|
||||
}
|
||||
|
||||
fun findAccountKeyByScreenName(context: Context, screenName: String): UserKey? {
|
||||
val am = AccountManager.get(context)
|
||||
for (account in AccountUtils.getAccounts(am)) {
|
||||
val user = account.getAccountUser(am)
|
||||
if (StringUtils.equalsIgnoreCase(screenName, user.screen_name)) {
|
||||
return user.key
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getAccountKeys(context: Context): Array<UserKey> {
|
||||
val am = AccountManager.get(context)
|
||||
val accounts = AccountUtils.getAccounts(am)
|
||||
val keys = ArrayList<UserKey>(accounts.size)
|
||||
for (account in accounts) {
|
||||
val keyString = am.getUserData(account, ACCOUNT_USER_DATA_KEY) ?: continue
|
||||
keys.add(UserKey.valueOf(keyString))
|
||||
}
|
||||
return keys.toTypedArray()
|
||||
}
|
||||
|
||||
fun findAccountKey(context: Context, accountId: String): UserKey? {
|
||||
val am = AccountManager.get(context)
|
||||
for (account in AccountUtils.getAccounts(am)) {
|
||||
val key = account.getAccountKey(am)
|
||||
if (accountId == key.id) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun hasAccount(context: Context): Boolean {
|
||||
return AccountUtils.getAccounts(AccountManager.get(context)).isNotEmpty()
|
||||
}
|
||||
|
||||
@Synchronized fun cleanDatabasesByItemLimit(context: Context) {
|
||||
val resolver = context.contentResolver
|
||||
val preferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||
val itemLimit = preferences[databaseItemLimitKey]
|
||||
|
||||
for (accountKey in getAccountKeys(context)) {
|
||||
// Clean statuses.
|
||||
for (uri in STATUSES_URIS) {
|
||||
if (CachedStatuses.CONTENT_URI == uri) {
|
||||
continue
|
||||
}
|
||||
val table = getTableNameByUri(uri)
|
||||
val qb = SQLSelectQuery.Builder()
|
||||
qb.select(Column(Statuses._ID))
|
||||
.from(Tables(table))
|
||||
.where(Expression.equalsArgs(Statuses.ACCOUNT_KEY))
|
||||
.orderBy(OrderBy(Statuses.POSITION_KEY, false))
|
||||
.limit(itemLimit)
|
||||
val where = Expression.and(
|
||||
Expression.notIn(Column(Statuses._ID), qb.build()),
|
||||
Expression.equalsArgs(Statuses.ACCOUNT_KEY)
|
||||
)
|
||||
val whereArgs = arrayOf(accountKey.toString(), accountKey.toString())
|
||||
resolver.delete(uri, where.sql, whereArgs)
|
||||
}
|
||||
for (uri in ACTIVITIES_URIS) {
|
||||
val table = getTableNameByUri(uri)
|
||||
val qb = SQLSelectQuery.Builder()
|
||||
qb.select(Column(Activities._ID))
|
||||
.from(Tables(table))
|
||||
.where(Expression.equalsArgs(Activities.ACCOUNT_KEY))
|
||||
.orderBy(OrderBy(Activities.TIMESTAMP, false))
|
||||
.limit(itemLimit)
|
||||
val where = Expression.and(
|
||||
Expression.notIn(Column(Activities._ID), qb.build()),
|
||||
Expression.equalsArgs(Activities.ACCOUNT_KEY)
|
||||
)
|
||||
val whereArgs = arrayOf(accountKey.toString(), accountKey.toString())
|
||||
resolver.delete(uri, where.sql, whereArgs)
|
||||
}
|
||||
}
|
||||
// Clean cached values.
|
||||
for (uri in CACHE_URIS) {
|
||||
val table = getTableNameByUri(uri) ?: continue
|
||||
val qb = SQLSelectQuery.Builder()
|
||||
qb.select(Column(BaseColumns._ID))
|
||||
.from(Tables(table))
|
||||
.orderBy(OrderBy(BaseColumns._ID, false))
|
||||
.limit(itemLimit * 20)
|
||||
val where = Expression.notIn(Column(BaseColumns._ID), qb.build())
|
||||
resolver.delete(uri, where.sql, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun isFilteringUser(context: Context, userKey: UserKey): Boolean {
|
||||
return isFilteringUser(context, userKey.toString())
|
||||
}
|
||||
|
||||
fun isFilteringUser(context: Context, userKey: String): Boolean {
|
||||
val cr = context.contentResolver
|
||||
val where = Expression.equalsArgs(Filters.Users.USER_KEY)
|
||||
val c = cr.query(Filters.Users.CONTENT_URI, arrayOf(SQLFunctions.COUNT()),
|
||||
where.sql, arrayOf(userKey), null) ?: return false
|
||||
try {
|
||||
if (c.moveToFirst()) {
|
||||
return c.getLong(0) > 0
|
||||
}
|
||||
} finally {
|
||||
c.close()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getStringFieldArray(context: Context, uri: Uri,
|
||||
keys: Array<UserKey?>, keyField: String,
|
||||
valueField: String, sortExpression: OrderBy?,
|
||||
extraHaving: Expression?, extraHavingArgs: Array<String>?): Array<String?> {
|
||||
return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving,
|
||||
extraHavingArgs, object : FieldArrayCreator<Array<String?>> {
|
||||
override fun newArray(size: Int): Array<String?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
override fun assign(array: Array<String?>, arrayIdx: Int, cur: Cursor, colIdx: Int) {
|
||||
array[arrayIdx] = cur.getString(colIdx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getLongFieldArray(context: Context, uri: Uri,
|
||||
keys: Array<UserKey?>, keyField: String,
|
||||
valueField: String, sortExpression: OrderBy?,
|
||||
extraHaving: Expression?, extraHavingArgs: Array<String>?): LongArray {
|
||||
return getFieldArray(context, uri, keys, keyField, valueField, sortExpression, extraHaving,
|
||||
extraHavingArgs, object : FieldArrayCreator<LongArray> {
|
||||
override fun newArray(size: Int): LongArray {
|
||||
return LongArray(size)
|
||||
}
|
||||
|
||||
override fun assign(array: LongArray, arrayIdx: Int, cur: Cursor, colIdx: Int) {
|
||||
array[arrayIdx] = cur.getLong(colIdx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
private fun <T> getFieldArray(context: Context, uri: Uri,
|
||||
keys: Array<UserKey?>, keyField: String,
|
||||
valueField: String, sortExpression: OrderBy?,
|
||||
extraHaving: Expression?, extraHavingArgs: Array<String>?,
|
||||
creator: FieldArrayCreator<T>): T {
|
||||
val resolver = context.contentResolver
|
||||
val resultArray = creator.newArray(keys.size)
|
||||
val nonNullKeys = keys.mapNotNull { it?.toString() }.toTypedArray()
|
||||
val tableName = getTableNameByUri(uri) ?: throw NullPointerException()
|
||||
val having: Expression
|
||||
if (extraHaving != null) {
|
||||
having = Expression.and(extraHaving, Expression.inArgs(keyField, nonNullKeys.size))
|
||||
} else {
|
||||
having = Expression.inArgs(keyField, nonNullKeys.size)
|
||||
}
|
||||
val havingArgs: Array<String>
|
||||
if (extraHavingArgs != null) {
|
||||
havingArgs = extraHavingArgs + nonNullKeys
|
||||
} else {
|
||||
havingArgs = nonNullKeys
|
||||
}
|
||||
val builder = SQLQueryBuilder.select(Columns(keyField, valueField))
|
||||
.from(Table(tableName))
|
||||
.groupBy(Column(keyField))
|
||||
.having(having)
|
||||
if (sortExpression != null) {
|
||||
builder.orderBy(sortExpression)
|
||||
}
|
||||
val rawUri = Uri.withAppendedPath(TwidereDataStore.CONTENT_URI_RAW_QUERY, builder.buildSQL())
|
||||
resolver.query(rawUri, null, null, havingArgs, null)?.useCursor { cur ->
|
||||
cur.moveToFirst()
|
||||
while (!cur.isAfterLast) {
|
||||
val string = cur.getString(0)
|
||||
if (string != null) {
|
||||
val accountKey = UserKey.valueOf(string)
|
||||
val idx = ArrayUtils.indexOf(keys, accountKey)
|
||||
if (idx >= 0) {
|
||||
creator.assign(resultArray, idx, cur, 1)
|
||||
}
|
||||
}
|
||||
cur.moveToNext()
|
||||
}
|
||||
}
|
||||
return resultArray
|
||||
}
|
||||
|
||||
fun deleteStatus(cr: ContentResolver, accountKey: UserKey,
|
||||
statusId: String, status: ParcelableStatus?) {
|
||||
|
||||
val host = accountKey.host
|
||||
val deleteWhere: String
|
||||
val updateWhere: String
|
||||
val deleteWhereArgs: Array<String>
|
||||
val updateWhereArgs: Array<String>
|
||||
if (host != null) {
|
||||
deleteWhere = Expression.and(
|
||||
Expression.likeRaw(Column(Statuses.ACCOUNT_KEY), "'%@'||?"),
|
||||
Expression.or(
|
||||
Expression.equalsArgs(Statuses.STATUS_ID),
|
||||
Expression.equalsArgs(Statuses.RETWEET_ID)
|
||||
)).sql
|
||||
deleteWhereArgs = arrayOf(host, statusId, statusId)
|
||||
updateWhere = Expression.and(
|
||||
Expression.likeRaw(Column(Statuses.ACCOUNT_KEY), "'%@'||?"),
|
||||
Expression.equalsArgs(Statuses.MY_RETWEET_ID)
|
||||
).sql
|
||||
updateWhereArgs = arrayOf(host, statusId)
|
||||
} else {
|
||||
deleteWhere = Expression.or(
|
||||
Expression.equalsArgs(Statuses.STATUS_ID),
|
||||
Expression.equalsArgs(Statuses.RETWEET_ID)
|
||||
).sql
|
||||
deleteWhereArgs = arrayOf(statusId, statusId)
|
||||
updateWhere = Expression.equalsArgs(Statuses.MY_RETWEET_ID).sql
|
||||
updateWhereArgs = arrayOf(statusId)
|
||||
}
|
||||
for (uri in STATUSES_URIS) {
|
||||
cr.delete(uri, deleteWhere, deleteWhereArgs)
|
||||
if (status != null) {
|
||||
val values = ContentValues()
|
||||
values.putNull(Statuses.MY_RETWEET_ID)
|
||||
values.put(Statuses.RETWEET_COUNT, status.retweet_count - 1)
|
||||
cr.update(uri, values, updateWhere, updateWhereArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun processTabExtras(expressions: MutableList<Expression>, expressionArgs: MutableList<String>, extras: HomeTabExtras) {
|
||||
if (extras.isHideRetweets) {
|
||||
expressions.add(Expression.equalsArgs(Statuses.IS_RETWEET))
|
||||
expressionArgs.add("0")
|
||||
}
|
||||
if (extras.isHideQuotes) {
|
||||
expressions.add(Expression.equalsArgs(Statuses.IS_QUOTE))
|
||||
expressionArgs.add("0")
|
||||
}
|
||||
if (extras.isHideReplies) {
|
||||
expressions.add(Expression.isNull(Column(Statuses.IN_REPLY_TO_STATUS_ID)))
|
||||
}
|
||||
}
|
||||
|
||||
fun prepareDatabase(context: Context) {
|
||||
val cr = context.contentResolver
|
||||
val cursor = cr.query(TwidereDataStore.CONTENT_URI_DATABASE_PREPARE, null, null,
|
||||
null, null) ?: return
|
||||
cursor.close()
|
||||
}
|
||||
|
||||
internal interface FieldArrayCreator<T> {
|
||||
fun newArray(size: Int): T
|
||||
|
||||
fun assign(array: T, arrayIdx: Int, cur: Cursor, colIdx: Int)
|
||||
}
|
||||
|
||||
fun queryCount(context: Context, uri: Uri,
|
||||
selection: String?, selectionArgs: Array<String>?): Int {
|
||||
val resolver = context.contentResolver
|
||||
val projection = arrayOf(SQLFunctions.COUNT())
|
||||
val cur = resolver.query(uri, projection, selection, selectionArgs, null) ?: return -1
|
||||
try {
|
||||
if (cur.moveToFirst()) {
|
||||
return cur.getInt(0)
|
||||
}
|
||||
return -1
|
||||
} finally {
|
||||
cur.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun getInteractionsCount(context: Context, extraArgs: Bundle?,
|
||||
accountIds: Array<UserKey>, since: Long, sinceColumn: String): Int {
|
||||
var extraWhere: Expression? = null
|
||||
var extraWhereArgs: Array<String>? = null
|
||||
var followingOnly = false
|
||||
if (extraArgs != null) {
|
||||
val extras = extraArgs.getParcelable<TabExtras>(IntentConstants.EXTRA_EXTRAS)
|
||||
if (extras is InteractionsTabExtras) {
|
||||
if (extras.isMentionsOnly) {
|
||||
extraWhere = Expression.inArgs(Activities.ACTION, 3)
|
||||
extraWhereArgs = arrayOf(Activity.Action.MENTION, Activity.Action.REPLY, Activity.Action.QUOTE)
|
||||
}
|
||||
if (extras.isMyFollowingOnly) {
|
||||
followingOnly = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return getActivitiesCount(context, Activities.AboutMe.CONTENT_URI, extraWhere, extraWhereArgs,
|
||||
since, sinceColumn, followingOnly, accountIds)
|
||||
}
|
||||
|
||||
fun addToFilter(context: Context, users: Collection<ParcelableUser>, filterAnywhere: Boolean) {
|
||||
val cr = context.contentResolver
|
||||
|
||||
try {
|
||||
val userValues = ArrayList<ContentValues>()
|
||||
val keywordValues = ArrayList<ContentValues>()
|
||||
val linkValues = ArrayList<ContentValues>()
|
||||
for (user in users) {
|
||||
val userItem = FiltersData.UserItem()
|
||||
userItem.userKey = user.key
|
||||
userItem.screenName = user.screen_name
|
||||
userItem.name = user.name
|
||||
userValues.add(`FiltersData$UserItemValuesCreator`.create(userItem))
|
||||
|
||||
val keywordItem = FiltersData.BaseItem()
|
||||
keywordItem.value = "@" + user.screen_name
|
||||
keywordValues.add(`FiltersData$BaseItemValuesCreator`.create(keywordItem))
|
||||
|
||||
// Insert user link (without scheme) to links
|
||||
val linkItem = FiltersData.BaseItem()
|
||||
val userLink = LinkCreator.getUserWebLink(user)
|
||||
val linkWithoutScheme = userLink.toString().substringAfter("://")
|
||||
linkItem.value = linkWithoutScheme
|
||||
linkValues.add(`FiltersData$BaseItemValuesCreator`.create(linkItem))
|
||||
}
|
||||
|
||||
ContentResolverUtils.bulkInsert(cr, Filters.Users.CONTENT_URI, userValues)
|
||||
if (filterAnywhere) {
|
||||
// Insert to filtered users
|
||||
ContentResolverUtils.bulkInsert(cr, Filters.Keywords.CONTENT_URI, keywordValues)
|
||||
// Insert user mention to keywords
|
||||
ContentResolverUtils.bulkInsert(cr, Filters.Links.CONTENT_URI, linkValues)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun removeFromFilter(context: Context, users: Collection<ParcelableUser>) {
|
||||
val userKeyValues = ArrayList<String>()
|
||||
val linkValues = ArrayList<String>()
|
||||
val keywordValues = ArrayList<String>()
|
||||
val cr = context.contentResolver
|
||||
for (user in users) {
|
||||
// Delete from filtered users
|
||||
userKeyValues.add(user.key.toString())
|
||||
// Delete user mention from keywords
|
||||
keywordValues.add("@" + user.screen_name)
|
||||
|
||||
// Delete user link (without scheme) from links
|
||||
val userLink = LinkCreator.getUserWebLink(user)
|
||||
val linkWithoutScheme = userLink.toString().substringAfter("://")
|
||||
linkValues.add(linkWithoutScheme)
|
||||
}
|
||||
ContentResolverUtils.bulkDelete(cr, Filters.Users.CONTENT_URI, Filters.Users.USER_KEY, false, userKeyValues, null)
|
||||
ContentResolverUtils.bulkDelete(cr, Filters.Keywords.CONTENT_URI, Filters.Keywords.VALUE, false, keywordValues, null)
|
||||
ContentResolverUtils.bulkDelete(cr, Filters.Links.CONTENT_URI, Filters.Links.VALUE, false, linkValues, null)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun findStatusInDatabases(context: Context,
|
||||
accountKey: UserKey,
|
||||
statusId: String): ParcelableStatus? {
|
||||
val resolver = context.contentResolver
|
||||
var status: ParcelableStatus? = null
|
||||
val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Statuses.STATUS_ID)).sql
|
||||
val whereArgs = arrayOf(accountKey.toString(), statusId)
|
||||
for (uri in DataStoreUtils.STATUSES_URIS) {
|
||||
val cur = resolver.query(uri, Statuses.COLUMNS, where, whereArgs, null) ?: continue
|
||||
try {
|
||||
if (cur.count > 0 && cur.moveToFirst()) {
|
||||
status = ParcelableStatusCursorIndices.fromCursor(cur)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Ignore
|
||||
} finally {
|
||||
cur.close()
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
|
||||
@WorkerThread
|
||||
@Throws(MicroBlogException::class)
|
||||
fun findStatus(context: Context, accountKey: UserKey, statusId: String): ParcelableStatus {
|
||||
val cached = findStatusInDatabases(context, accountKey, statusId)
|
||||
if (cached != null) return cached
|
||||
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: throw MicroBlogException("Account does not exist")
|
||||
val result = twitter.showStatus(statusId)
|
||||
val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Statuses.STATUS_ID)).sql
|
||||
val whereArgs = arrayOf(accountKey.toString(), statusId)
|
||||
val resolver = context.contentResolver
|
||||
val status = ParcelableStatusUtils.fromStatus(result, accountKey, false)
|
||||
resolver.delete(CachedStatuses.CONTENT_URI, where, whereArgs)
|
||||
try {
|
||||
resolver.insert(CachedStatuses.CONTENT_URI, ParcelableStatusValuesCreator.create(status))
|
||||
} catch (e: IOException) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
}
|
|
@ -6,13 +6,15 @@ import com.squareup.otto.Bus
|
|||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.KPreferences
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.twidere.constant.IntentConstants.INTENT_PACKAGE_PREFIX
|
||||
import org.mariotaku.twidere.constant.dataSyncProviderInfoKey
|
||||
import org.mariotaku.twidere.constant.stopAutoRefreshWhenBatteryLowKey
|
||||
import org.mariotaku.twidere.model.AccountPreferences
|
||||
import org.mariotaku.twidere.model.SimpleRefreshTaskParam
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.task.GetActivitiesAboutMeTask
|
||||
import org.mariotaku.twidere.task.GetHomeTimelineTask
|
||||
import org.mariotaku.twidere.task.GetMessagesTask
|
||||
|
@ -54,21 +56,22 @@ class TaskServiceRunner(
|
|||
ACTION_REFRESH_HOME_TIMELINE -> {
|
||||
val task = GetHomeTimelineTask(context)
|
||||
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
|
||||
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys)
|
||||
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys.toNulls())
|
||||
}
|
||||
return task
|
||||
}
|
||||
ACTION_REFRESH_NOTIFICATIONS -> {
|
||||
val task = GetActivitiesAboutMeTask(context)
|
||||
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
|
||||
DataStoreUtils.getNewestActivityMaxPositions(context, Activities.AboutMe.CONTENT_URI, accountKeys)
|
||||
DataStoreUtils.getNewestActivityMaxPositions(context, Activities.AboutMe.CONTENT_URI,
|
||||
accountKeys.toNulls())
|
||||
}
|
||||
return task
|
||||
}
|
||||
ACTION_REFRESH_DIRECT_MESSAGES -> {
|
||||
val task = GetMessagesTask(context)
|
||||
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
|
||||
DataStoreUtils.getNewestMessageIds(context, DirectMessages.Inbox.CONTENT_URI, accountKeys)
|
||||
arrayOfNulls(accountKeys.size)
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
@ -90,11 +93,10 @@ class TaskServiceRunner(
|
|||
val refreshable: (AccountPreferences) -> Boolean,
|
||||
val getSinceIds: (Array<UserKey>) -> Array<String?>?
|
||||
) : SimpleRefreshTaskParam() {
|
||||
override fun getAccountKeysWorker(): Array<UserKey> {
|
||||
val prefs = AccountPreferences.getAccountPreferences(context,
|
||||
DataStoreUtils.getAccountKeys(context)).filter(AccountPreferences::isAutoRefreshEnabled)
|
||||
return prefs.filter(refreshable)
|
||||
.map(AccountPreferences::getAccountKey).toTypedArray()
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
return@lazy AccountPreferences.getAccountPreferences(context, DataStoreUtils.getAccountKeys(context)).filter {
|
||||
it.isAutoRefreshEnabled && refreshable(it)
|
||||
}.map(AccountPreferences::getAccountKey).toTypedArray()
|
||||
}
|
||||
|
||||
override val sinceIds: Array<String?>?
|
||||
|
|
Loading…
Reference in New Issue