1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-02 17:56:56 +01:00

improved conversation loader

This commit is contained in:
Mariotaku Lee 2016-01-03 12:43:08 +08:00
parent 4a4defaa5d
commit 94788c7c3c
36 changed files with 548 additions and 713 deletions

View File

@ -86,19 +86,19 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
@ParcelableThisPlease
@JsonField(name = "user_id")
@CursorField(Statuses.USER_ID)
public long user_id;
public long user_id = -1;
@ParcelableThisPlease
@JsonField(name = "retweet_id")
@CursorField(Statuses.RETWEET_ID)
public long retweet_id;
public long retweet_id = -1;
@ParcelableThisPlease
@JsonField(name = "retweeted_by_user_id")
@CursorField(Statuses.RETWEETED_BY_USER_ID)
public long retweeted_by_user_id;
public long retweeted_by_user_id = -1;
@ParcelableThisPlease
@JsonField(name = "retweet_timestamp")
@CursorField(Statuses.RETWEET_TIMESTAMP)
public long retweet_timestamp;
public long retweet_timestamp = -1;
@ParcelableThisPlease
@JsonField(name = "retweet_count")
@CursorField(Statuses.RETWEET_COUNT)

View File

@ -36,4 +36,9 @@ public class Nullables {
return list;
}
@NonNull
public static <T> T assertNonNull(@Nullable T object) {
if (object == null) throw new NullPointerException();
return object;
}
}

View File

@ -171,12 +171,18 @@ public class TwitterContentUtils {
}
private static final CharSequenceTranslator UNESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_UNESCAPE());
private static final CharSequenceTranslator ESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_ESCAPE());
public static String unescapeTwitterStatusText(final CharSequence text) {
if (text == null) return null;
return UNESCAPE_TWITTER_RAW_TEXT.translate(text);
}
public static String escapeTwitterStatusText(final CharSequence text) {
if (text == null) return null;
return ESCAPE_TWITTER_RAW_TEXT.translate(text);
}
public static <T extends List<Status>> T getStatusesWithQuoteData(Twitter twitter, @NonNull T list) throws TwitterException {
LongSparseMap<Status> quotes = new LongSparseMap<>();
// Phase 1: collect all statuses contains a status link, and put it in the map

View File

@ -27,7 +27,9 @@ import android.content.SharedPreferences;
import android.location.Location;
import android.os.BatteryManager;
import android.text.TextUtils;
import android.util.Log;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.util.JsonSerializer;
@ -58,6 +60,7 @@ import edu.tsinghua.hotmobi.model.ScreenEvent;
import edu.tsinghua.hotmobi.model.ScrollRecord;
import edu.tsinghua.hotmobi.model.SessionEvent;
import edu.tsinghua.hotmobi.model.TweetEvent;
import edu.tsinghua.hotmobi.model.UploadLogEvent;
/**
* Created by mariotaku on 15/8/10.
@ -107,6 +110,8 @@ public class HotMobiLogger {
return "notification";
} else if (event instanceof ScreenEvent) {
return "screen";
} else if (event instanceof UploadLogEvent) {
return "upload_log";
}
throw new UnsupportedOperationException("Unknown event type " + event);
}
@ -187,6 +192,18 @@ public class HotMobiLogger {
getInstance(context).log(record, null);
}
public static boolean log(final String msg) {
if (BuildConfig.DEBUG) {
final StackTraceElement ste = new Throwable().fillInStackTrace().getStackTrace()[1];
final String fullName = ste.getClassName();
final String name = fullName.substring(fullName.lastIndexOf('.'));
final String tag = name + "." + ste.getMethodName();
Log.d(tag, msg);
return true;
} else
return false;
}
public <T> void log(long accountId, final T event, final PreProcessing<T> preProcessing) {
mExecutor.execute(new WriteLogTask<>(mApplication, accountId, event, preProcessing));
}

View File

@ -41,7 +41,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
import edu.tsinghua.hotmobi.model.UploadLogEvent;
/**
* Upload logs to target server
@ -67,7 +67,7 @@ public class UploadLogsTask implements Runnable {
final double deltaDays = (System.currentTimeMillis() - lastUpload) /
(double) HotMobiLogger.UPLOAD_INTERVAL_MILLIS;
if (deltaDays < 1) {
SpiceProfilingUtil.log("Last uploaded was conducted in 1 day ago.");
HotMobiLogger.log("Last uploaded was conducted in 1 day ago.");
return;
}
}
@ -106,8 +106,13 @@ public class UploadLogsTask implements Runnable {
builder.headers(headers);
body = new FileTypedData(logFile);
builder.body(body);
final UploadLogEvent uploadLogEvent = UploadLogEvent.create(context, logFile);
response = client.execute(builder.build());
if (response.isSuccessful()) {
uploadLogEvent.markEnd();
if (!uploadLogEvent.shouldSkip()) {
HotMobiLogger.getInstance(context).log(uploadLogEvent);
}
succeeded &= logFile.delete();
}
} catch (IOException e) {

View File

@ -31,7 +31,7 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.DataStoreUtils;
import java.util.HashMap;
@ -92,7 +92,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, Utils.getAccountIds(context))) {
for (AccountPreferences pref : AccountPreferences.getAccountPreferences(context, DataStoreUtils.getAccountIds(context))) {
final long accountId = pref.getAccountId();
preferences.put("notification_" + accountId + "_home", String.valueOf(pref.isHomeTimelineNotificationEnabled()));
preferences.put("notification_" + accountId + "_interactions", String.valueOf(pref.isInteractionsNotificationEnabled()));

View File

@ -0,0 +1,77 @@
package edu.tsinghua.hotmobi.model;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import java.io.File;
/**
* Created by mariotaku on 16/1/2.
*/
@ParcelablePlease
@JsonObject
public class UploadLogEvent extends BaseEvent implements Parcelable {
public static final Creator<UploadLogEvent> CREATOR = new Creator<UploadLogEvent>() {
public UploadLogEvent createFromParcel(Parcel source) {
UploadLogEvent target = new UploadLogEvent();
UploadLogEventParcelablePlease.readFromParcel(target, source);
return target;
}
public UploadLogEvent[] newArray(int size) {
return new UploadLogEvent[size];
}
};
@ParcelableThisPlease
@JsonField(name = "file_name")
String fileName;
@ParcelableThisPlease
@JsonField(name = "file_length")
long fileLength;
public long getFileLength() {
return fileLength;
}
public void setFileLength(long fileLength) {
this.fileLength = fileLength;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
UploadLogEventParcelablePlease.writeToParcel(this, dest, flags);
}
public static UploadLogEvent create(Context context, File file) {
UploadLogEvent event = new UploadLogEvent();
event.markStart(context);
event.setFileLength(file.length());
event.setFileName(file.getName());
return event;
}
public boolean shouldSkip() {
return fileName.contains("upload_log");
}
}

View File

@ -1,23 +0,0 @@
package edu.tsinghua.spice.Task;
import java.io.File;
import java.io.FileFilter;
/**
* Created by Denny C. Ng on 2/21/15.
*/
public final class SpiceFileFilter implements FileFilter {
@Override
public boolean accept(final File file) {
return file.isFile() && "spi".equalsIgnoreCase(getExtension(file));
}
static String getExtension(final File file) {
final String name = file.getName();
final int pos = name.lastIndexOf('.');
if (pos == -1) return null;
return name.substring(pos + 1);
}
}

View File

@ -1,48 +0,0 @@
package edu.tsinghua.spice.Utilies;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.util.Log;
import org.mariotaku.twidere.BuildConfig;
/**
* Created by Denny C. Ng on 2/20/15.
*/
public class SpiceProfilingUtil {
public static final String FILE_NAME_PROFILE = "Profile_SPICE";
public static final String FILE_NAME_LOCATION = "Location_SPICE";
public static final String FILE_NAME_APP = "App_SPICE";
public static final String FILE_NAME_NETWORK = "Network_SPICE";
public static final String FILE_NAME_ONWIFI = "onWifi_SPICE";
public static final String FILE_NAME_ONLAUNCH = "onLaunch_SPICE";
public static final String FILE_NAME_SCREEN = "Screen_SPICE";
@SuppressLint("InlinedApi")
public static boolean isCharging(final Context context) {
if (context == null) return false;
final Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (intent == null) return false;
final int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB
|| plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
public static boolean log(final String msg) {
if (BuildConfig.DEBUG) {
final StackTraceElement ste = new Throwable().fillInStackTrace().getStackTrace()[1];
final String fullName = ste.getClassName();
final String name = fullName.substring(fullName.lastIndexOf('.'));
final String tag = name + "." + ste.getMethodName();
Log.d(tag, msg);
return true;
} else
return false;
}
}

View File

@ -474,7 +474,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
intent.putExtra(EXTRA_ACCOUNT_IDS, accountIds);
if (accountIds.length > 0) {
final long account_id = accountIds[0];
intent.putExtra(EXTRA_NAME, Utils.getAccountName(this, account_id));
intent.putExtra(EXTRA_NAME, DataStoreUtils.getAccountName(this, account_id));
intent.putExtra(EXTRA_SCREEN_NAME, DataStoreUtils.getAccountScreenName(this, account_id));
}
if (mInReplyToStatusId > 0) {
@ -605,7 +605,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
mValidator = new TwidereValidator(this);
setContentView(R.layout.activity_compose);
setFinishOnTouchOutside(false);
final long[] defaultAccountIds = Utils.getAccountIds(this);
final long[] defaultAccountIds = DataStoreUtils.getAccountIds(this);
if (defaultAccountIds.length <= 0) {
final Intent intent = new Intent(INTENT_ACTION_TWITTER_LOGIN);
intent.setClass(this, SignInActivity.class);

View File

@ -347,7 +347,7 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
}
mMultiSelectHandler = new MultiSelectEventHandler(this);
mMultiSelectHandler.dispatchOnCreate();
if (!Utils.hasAccount(this)) {
if (!DataStoreUtils.hasAccount(this)) {
final Intent signInIntent = new Intent(INTENT_ACTION_TWITTER_LOGIN);
signInIntent.setClass(this, SignInActivity.class);
startActivity(signInIntent);

View File

@ -33,6 +33,7 @@ import org.mariotaku.twidere.model.DraftItemCursorIndices;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMediaUpdate;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.MediaLoadingHandler;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
@ -43,8 +44,6 @@ import org.mariotaku.twidere.view.holder.DraftViewHolder;
import javax.inject.Inject;
import static org.mariotaku.twidere.util.Utils.getAccountColors;
public class DraftsAdapter extends SimpleCursorAdapter implements Constants {
@Inject
@ -81,7 +80,7 @@ public class DraftsAdapter extends SimpleCursorAdapter implements Constants {
} else {
holder.media_preview_container.setVisibility(View.GONE);
}
holder.content.drawEnd(getAccountColors(context, accountIds));
holder.content.drawEnd(DataStoreUtils.getAccountColors(context, accountIds));
holder.setTextSize(mTextSize);
final boolean emptyContent = TextUtils.isEmpty(text);
if (emptyContent) {

View File

@ -0,0 +1,65 @@
package org.mariotaku.twidere.api.twitter.model;
import android.support.annotation.NonNull;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.TwitterContentUtils;
import java.util.Date;
/**
* Created by mariotaku on 16/1/3.
*/
public class StatusUtils {
public static Status fromParcelableStatus(@NonNull ParcelableStatus parcelable) {
Status status = new Status();
status.id = parcelable.id;
status.text = TwitterContentUtils.escapeTwitterStatusText(parcelable.text_plain);
status.createdAt = new Date(parcelable.timestamp);
status.inReplyToStatusId = parcelable.in_reply_to_status_id;
status.inReplyToUserId = parcelable.in_reply_to_user_id;
status.inReplyToScreenName = parcelable.in_reply_to_screen_name;
if (parcelable.is_retweet) {
Status retweet = status.retweetedStatus = new Status();
retweet.id = parcelable.retweet_id;
retweet.text = TwitterContentUtils.escapeTwitterStatusText(parcelable.text_plain);
retweet.createdAt = new Date(parcelable.retweet_timestamp);
User retweetUser = retweet.user = new User();
retweetUser.id = parcelable.user_id;
retweetUser.screenName = parcelable.user_screen_name;
retweetUser.name = parcelable.user_name;
retweetUser.profileBackgroundImageUrl = parcelable.user_profile_image_url;
User user = status.user = new User();
user.id = parcelable.retweeted_by_user_id;
user.name = parcelable.retweeted_by_user_name;
user.screenName = parcelable.retweeted_by_user_screen_name;
user.profileImageUrl = parcelable.retweeted_by_user_profile_image;
} else if (parcelable.is_quote) {
Status quote = status.quotedStatus = new Status();
quote.id = parcelable.quoted_id;
quote.text = TwitterContentUtils.escapeTwitterStatusText(parcelable.quoted_text_plain);
quote.createdAt = new Date(parcelable.quoted_timestamp);
User quotedUser = quote.user = new User();
quotedUser.id = parcelable.quoted_user_id;
quotedUser.name = parcelable.quoted_user_name;
quotedUser.screenName = parcelable.quoted_user_screen_name;
quotedUser.profileImageUrl = parcelable.quoted_user_profile_image;
User user = status.user = new User();
user.id = parcelable.user_id;
user.screenName = parcelable.user_screen_name;
user.name = parcelable.user_name;
user.profileBackgroundImageUrl = parcelable.user_profile_image_url;
} else {
User user = status.user = new User();
user.id = parcelable.user_id;
user.screenName = parcelable.user_screen_name;
user.name = parcelable.user_name;
user.profileBackgroundImageUrl = parcelable.user_profile_image_url;
}
return status;
}
}

View File

@ -64,6 +64,7 @@ import org.mariotaku.twidere.activity.support.CustomTabEditorActivity;
import org.mariotaku.twidere.model.CustomTabConfiguration;
import org.mariotaku.twidere.model.CustomTabConfiguration.CustomTabConfigurationComparator;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.TwoLineWithIconViewHolder;
@ -80,7 +81,6 @@ import static org.mariotaku.twidere.util.CustomTabUtils.getTabIconObject;
import static org.mariotaku.twidere.util.CustomTabUtils.getTabTypeName;
import static org.mariotaku.twidere.util.CustomTabUtils.isTabAdded;
import static org.mariotaku.twidere.util.CustomTabUtils.isTabTypeValid;
import static org.mariotaku.twidere.util.Utils.getAccountIds;
public class CustomTabsFragment extends BaseFragment implements LoaderCallbacks<Cursor>,
MultiChoiceModeListener, OnItemClickListener {
@ -221,7 +221,7 @@ public class CustomTabsFragment extends BaseFragment implements LoaderCallbacks<
final Resources res = getResources();
final boolean hasOfficialKeyAccounts = Utils.hasAccountSignedWithOfficialKeys(getActivity());
final boolean forcePrivateAPI = mPreferences.getBoolean(KEY_FORCE_USING_PRIVATE_APIS, false);
final long[] accountIds = getAccountIds(getActivity());
final long[] accountIds = DataStoreUtils.getAccountIds(getActivity());
final MenuItem itemAdd = menu.findItem(R.id.add_submenu);
if (itemAdd != null && itemAdd.hasSubMenu()) {
final SubMenu subMenu = itemAdd.getSubMenu();

View File

@ -111,6 +111,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.CompareUtils;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.HtmlSpanBuilder;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
@ -197,14 +198,13 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
mStatusAdapter.setRepliesLoading(true);
mStatusAdapter.setConversationsLoading(true);
mStatusAdapter.updateItemDecoration();
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final String screenName = args.getString(EXTRA_SCREEN_NAME);
final long statusId = args.getLong(EXTRA_STATUS_ID, -1);
final ParcelableStatus status = args.getParcelable(EXTRA_STATUS);
final long maxId = args.getLong(EXTRA_MAX_ID, -1);
final long sinceId = args.getLong(EXTRA_SINCE_ID, -1);
final boolean twitterOptimizedSearches = mPreferences.getBoolean(KEY_TWITTER_OPTIMIZED_SEARCHES);
final ConversationLoader loader = new ConversationLoader(getActivity(), accountId,
statusId, screenName, sinceId, maxId, null, true);
assert status != null;
final ConversationLoader loader = new ConversationLoader(getActivity(), status, sinceId,
maxId, null, true, twitterOptimizedSearches);
loader.setComparator(ParcelableStatus.REVERSE_ID_COMPARATOR);
return loader;
}
@ -618,7 +618,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
final Bundle args = new Bundle();
args.putLong(EXTRA_ACCOUNT_ID, status.account_id);
args.putLong(EXTRA_STATUS_ID, status.is_retweet ? status.retweet_id : status.id);
args.putString(EXTRA_SCREEN_NAME, status.is_retweet ? status.retweeted_by_user_screen_name : status.user_screen_name);
args.putParcelable(EXTRA_STATUS, status);
if (mConversationLoaderInitialized) {
getLoaderManager().restartLoader(LOADER_ID_STATUS_CONVERSATIONS, args, mConversationsLoaderCallback);
return;
@ -917,7 +917,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
retweetedByView.setVisibility(View.GONE);
}
profileContainer.drawEnd(Utils.getAccountColor(context, status.account_id));
profileContainer.drawEnd(DataStoreUtils.getAccountColor(context, status.account_id));
final int layoutPosition = getLayoutPosition();
if (status.is_quote && ArrayUtils.isEmpty(status.media)) {
@ -1542,16 +1542,17 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
final ParcelableStatus status = mStatus;
if (status == null) return;
mData = data;
if (data == null) {
if (data == null || data.isEmpty()) {
setCount(ITEM_IDX_CONVERSATION, 0);
setCount(ITEM_IDX_REPLY, 0);
} else {
int conversationCount = 0, replyCount = 0;
boolean containsStatus = false;
final long statusId = status.is_retweet ? status.retweet_id : status.id;
for (ParcelableStatus item : data) {
if (item.id < status.id) {
if (item.id < statusId) {
conversationCount++;
} else if (item.id > status.id) {
} else if (item.id > statusId) {
replyCount++;
} else {
containsStatus = true;

View File

@ -493,7 +493,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
mUser = user;
final int userColor = mUserColorNameManager.getUserColor(user.id, true);
mProfileImageView.setBorderColor(userColor != 0 ? userColor : Color.WHITE);
mProfileNameContainer.drawEnd(Utils.getAccountColor(activity, user.account_id));
mProfileNameContainer.drawEnd(DataStoreUtils.getAccountColor(activity, user.account_id));
final String nick = mUserColorNameManager.getUserNickname(user.id, true);
mNameView.setText(TextUtils.isEmpty(nick) ? user.name : getString(R.string.name_with_nickname, user.name, nick));
final int typeIconRes = Utils.getUserTypeIconRes(user.is_verified, user.is_protected);
@ -840,7 +840,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
MenuUtils.setMenuItemAvailability(menu, R.id.incoming_friendships, isMyself);
MenuUtils.setMenuItemAvailability(menu, R.id.saved_searches, isMyself);
MenuUtils.setMenuItemAvailability(menu, R.id.scheduled_statuses, isMyself
&& Utils.getOfficialKeyType(getActivity(), user.account_id) == ConsumerKeyType.TWEETDECK);
&& TwitterAPIFactory.getOfficialKeyType(getActivity(), user.account_id) == ConsumerKeyType.TWEETDECK);
// final MenuItem followItem = menu.findItem(MENU_FOLLOW);
// followItem.setVisible(!isMyself);
// final boolean shouldShowFollowItem = !creatingFriendship && !destroyingFriendship && !isMyself
@ -1448,7 +1448,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
mPagerAdapter.addTab(UserTimelineFragment.class, tabArgs, getString(R.string.statuses),
R.drawable.ic_action_quote, TAB_TYPE_STATUSES, TAB_POSITION_STATUSES, null);
if (Utils.isOfficialKeyAccount(context, accountId)) {
if (TwitterAPIFactory.isOfficialKeyAccount(context, accountId)) {
mPagerAdapter.addTab(UserMediaTimelineFragment.class, tabArgs, getString(R.string.media),
R.drawable.ic_action_gallery, TAB_TYPE_MEDIA, TAB_POSITION_MEDIA, null);
}

View File

@ -1,53 +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.loader.support;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import org.mariotaku.twidere.model.ParcelableActivity;
import java.util.List;
import org.mariotaku.twidere.api.twitter.model.Activity;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
public class ActivitiesAboutMeLoader extends TwitterAPIActivitiesLoader {
public ActivitiesAboutMeLoader(final Context context, final long accountId, long sinceId,
long maxId, final List<ParcelableActivity> data,
final String[] saveFileArgs, final int position) {
super(context, accountId, sinceId, maxId, data, saveFileArgs, position);
}
@Override
protected List<Activity> getActivities(final Twitter twitter, final Paging paging) throws TwitterException {
if (twitter == null) return null;
return twitter.getActivitiesAboutMe(paging);
}
@Override
protected boolean shouldFilterActivity(SQLiteDatabase database, ParcelableActivity activity) {
return false;
}
}

View File

@ -1,55 +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.loader.support;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import org.mariotaku.twidere.model.ParcelableActivity;
import java.util.List;
import org.mariotaku.twidere.api.twitter.model.Activity;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
public class ActivitiesByFriendsLoader extends TwitterAPIActivitiesLoader {
public ActivitiesByFriendsLoader(final Context context, final long accountId, long sinceId,
long maxId, final List<ParcelableActivity> data,
final String[] saveFileArgs, final int position) {
super(context, accountId, sinceId, maxId, data, saveFileArgs, position);
}
@Override
protected List<Activity> getActivities(final Twitter twitter, final Paging paging) throws TwitterException {
if (twitter == null) return null;
return twitter.getActivitiesByFriends(paging);
}
@Override
protected boolean shouldFilterActivity(SQLiteDatabase database, ParcelableActivity activity) {
return false;
}
}

View File

@ -26,27 +26,74 @@ import android.support.annotation.NonNull;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.SearchQuery;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.StatusUtils;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
import org.mariotaku.twidere.util.Nullables;
import org.mariotaku.twidere.util.ParcelUtils;
import org.mariotaku.twidere.util.TwitterAPIFactory;
import org.mariotaku.twidere.util.Utils;
import java.util.ArrayList;
import java.util.List;
public class ConversationLoader extends TwitterAPIStatusesLoader {
private final long mInReplyToStatusId;
private final boolean mTwitterOptimizedSearches;
public ConversationLoader(final Context context, final long accountId, final long statusId,
final String screenName, final long sinceId, final long maxId,
final List<ParcelableStatus> data, final boolean fromUser) {
super(context, accountId, sinceId, maxId, data, null, -1, fromUser);
mInReplyToStatusId = statusId;
@NonNull
private final ParcelableStatus mStatus;
public ConversationLoader(final Context context, @NonNull final ParcelableStatus status,
final long sinceId, final long maxId, final List<ParcelableStatus> data,
final boolean fromUser, final boolean twitterOptimizedSearches) {
super(context, status.account_id, sinceId, maxId, data, null, -1, fromUser);
mStatus = Nullables.assertNonNull(ParcelUtils.clone(status));
ParcelableStatusUtils.makeOriginalStatus(mStatus);
mTwitterOptimizedSearches = twitterOptimizedSearches;
}
@NonNull
@Override
public List<Status> getStatuses(@NonNull final Twitter twitter, final Paging paging) throws TwitterException {
return twitter.showConversation(mInReplyToStatusId, paging);
final ParcelableStatus status = mStatus;
if (Utils.shouldForceUsingPrivateAPIs(getContext()) || TwitterAPIFactory.isOfficialTwitterInstance(getContext(), twitter)) {
return twitter.showConversation(status.id, paging);
}
final List<Status> statuses = new ArrayList<>();
final long maxId = getMaxId(), sinceId = getSinceId();
boolean getConversations = maxId < status.id || (maxId <= 0 && sinceId <= 0);
boolean getReplies = sinceId > status.id || (maxId <= 0 && sinceId <= 0);
if (getConversations) {
long inReplyToId = status.in_reply_to_status_id;
int count = 0;
while (inReplyToId > 0 && count < 10) {
final Status item = twitter.showStatus(inReplyToId);
inReplyToId = item.getInReplyToStatusId();
statuses.add(item);
count++;
}
}
if ((sinceId <= 0 && maxId <= 0) || (sinceId > 0 && sinceId < status.id) || (maxId > 0 && maxId >= status.id)) {
statuses.add(StatusUtils.fromParcelableStatus(status));
}
if (getReplies) {
SearchQuery query = new SearchQuery();
if (mTwitterOptimizedSearches) {
query.query("to:" + status.user_screen_name);
} else {
query.query("@" + status.user_screen_name);
}
query.sinceId(status.id);
for (Status item : twitter.search(query)) {
if (item.getInReplyToStatusId() == status.id) {
statuses.add(item);
}
}
}
return statuses;
}
@Override

View File

@ -23,17 +23,16 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import org.mariotaku.twidere.model.ParcelableStatus;
import java.util.List;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.DataStoreUtils;
import java.util.List;
import static org.mariotaku.twidere.util.Utils.getAccountId;
import static org.mariotaku.twidere.util.Utils.isFiltered;
public class MediaTimelineLoader extends TwitterAPIStatusesLoader {
@ -48,7 +47,7 @@ public class MediaTimelineLoader extends TwitterAPIStatusesLoader {
super(context, accountId, sinceId, maxId, data, savedStatusesArgs, tabPosition, fromUser);
mUserId = userId;
mUserScreenName = screenName;
mIsMyTimeline = userId > 0 ? accountId == userId : accountId == getAccountId(context, screenName);
mIsMyTimeline = userId > 0 ? accountId == userId : accountId == DataStoreUtils.getAccountId(context, screenName);
}
@NonNull

View File

@ -1,87 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.loader.support;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.util.collection.NoDuplicatesArrayList;
import java.util.List;
public abstract class ParcelableActivitiesLoader extends AsyncTaskLoader<List<ParcelableActivity>> implements Constants {
private final List<ParcelableActivity> mData = new NoDuplicatesArrayList<>();
private final boolean mFirstLoad;
private final int mTabPosition;
private Long mLastViewedId;
public ParcelableActivitiesLoader(final Context context, final List<ParcelableActivity> data, final int tab_position) {
super(context);
mFirstLoad = data == null;
if (data != null) {
mData.addAll(data);
}
mTabPosition = tab_position;
}
public Long getLastViewedId() {
return mLastViewedId;
}
protected boolean containsStatus(final long id) {
for (final ParcelableActivity activity : mData) {
if (activity.max_position <= id && activity.min_position >= id) return true;
}
return false;
}
protected boolean deleteActivity(final List<ParcelableActivity> activities, final long id) {
if (activities == null || activities.isEmpty()) return false;
boolean result = false;
for (final ParcelableActivity activity : activities.toArray(new ParcelableActivity[activities.size()])) {
if (id <= activity.max_position && id >= activity.min_position) {
result |= activities.remove(activity);
}
}
return result;
}
protected List<ParcelableActivity> getData() {
return mData;
}
protected int getTabPosition() {
return mTabPosition;
}
protected boolean isFirstLoad() {
return mFirstLoad;
}
@Override
protected void onStartLoading() {
forceLoad();
}
}

View File

@ -1,189 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.loader.support;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.util.Pair;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Activity;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.TwitterAPIFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.mariotaku.twidere.util.Utils.truncateActivities;
public abstract class TwitterAPIActivitiesLoader extends ParcelableActivitiesLoader {
private final Context mContext;
private final long mAccountIds;
private final long mMaxId, mSinceId;
private final Object[] mSavedStatusesFileArgs;
private Comparator<ParcelableActivity> mComparator;
public TwitterAPIActivitiesLoader(final Context context, final long accountId, final long sinceId,
final long maxId, final List<ParcelableActivity> data, final String[] savedStatusesArgs,
final int tabPosition) {
super(context, data, tabPosition);
mContext = context;
mAccountIds = accountId;
mSinceId = sinceId;
mMaxId = maxId;
mSavedStatusesFileArgs = savedStatusesArgs;
}
@SuppressWarnings("unchecked")
@Override
public final List<ParcelableActivity> loadInBackground() {
final File serializationFile = getSerializationFile();
final List<ParcelableActivity> data = getData();
if (isFirstLoad() && getTabPosition() >= 0 && serializationFile != null) {
final List<ParcelableActivity> cached = getCachedData(serializationFile);
if (cached != null) {
data.addAll(cached);
if (mComparator != null) {
Collections.sort(data, mComparator);
} else {
Collections.sort(data);
}
return new CopyOnWriteArrayList<>(data);
}
}
final List<Activity> activities;
final boolean truncated;
final Context context = getContext();
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final int loadItemLimit = prefs.getInt(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT);
try {
final Paging paging = new Paging();
paging.setCount(loadItemLimit);
if (mMaxId > 0) {
paging.setMaxId(mMaxId);
}
if (mSinceId > 0) {
paging.setSinceId(mSinceId);
}
activities = new ArrayList<>();
truncated = truncateActivities(getActivities(getTwitter(), paging), activities, mSinceId);
} catch (final TwitterException e) {
// mHandler.post(new ShowErrorRunnable(e));
Log.w(LOGTAG, e);
return new CopyOnWriteArrayList<>(data);
}
final Pair<Long, Long> position;
if (activities.isEmpty()) {
position = new Pair<>(-1L, -1L);
} else {
final Activity minActivity = Collections.min(activities);
position = new Pair<>(minActivity.getMinPosition(), minActivity.getMaxPosition());
}
final boolean insertGap = position.first > 0 && position.second > 0 && activities.size() > 1
&& !data.isEmpty() && !truncated;
// mHandler.post(CacheUsersStatusesTask.getRunnable(context, new StatusListResponse(mAccountIds, activities)));
for (final Activity activity : activities) {
final long min = activity.getMinPosition(), max = activity.getMaxPosition();
final boolean deleted = deleteActivity(data, max);
final boolean isGap = position.first == min && position.second == max && insertGap && !deleted;
data.add(new ParcelableActivity(activity, mAccountIds, isGap));
}
// final ParcelableActivity[] array = data.toArray(new ParcelableActivity[data.size()]);
// for (int i = 0, size = array.length; i < size; i++) {
// final ParcelableActivity status = array[i];
// if (shouldFilterActivity(mDatabase, status) && !status.is_gap && i != size - 1) {
// deleteActivity(data, status.max_position);
// }
// }
if (mComparator != null) {
Collections.sort(data, mComparator);
} else {
Collections.sort(data);
}
saveCachedData(serializationFile, data);
return new CopyOnWriteArrayList<>(data);
}
public final void setComparator(Comparator<ParcelableActivity> comparator) {
mComparator = comparator;
}
protected abstract List<Activity> getActivities(Twitter twitter, Paging paging) throws TwitterException;
protected final Twitter getTwitter() {
return TwitterAPIFactory.getTwitterInstance(mContext, mAccountIds, true, true);
}
protected abstract boolean shouldFilterActivity(final SQLiteDatabase database, final ParcelableActivity activity);
private List<ParcelableActivity> getCachedData(final File file) {
if (file == null) return null;
try {
return JsonSerializer.parseList(file, ParcelableActivity.class);
} catch (final IOException e) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e);
}
} catch (RuntimeException e) {
if (BuildConfig.DEBUG) {
throw e;
}
Log.e(LOGTAG, "Error unserializing data", e);
}
return null;
}
private File getSerializationFile() {
if (mSavedStatusesFileArgs == null) return null;
try {
return JsonSerializer.getSerializationFile(mContext, mSavedStatusesFileArgs);
} catch (final IOException e) {
return null;
}
}
private void saveCachedData(final File file, final List<ParcelableActivity> data) {
if (file == null || data == null) return;
final SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final int databaseItemLimit = prefs.getInt(KEY_DATABASE_ITEM_LIMIT, DEFAULT_DATABASE_ITEM_LIMIT);
try {
final List<ParcelableActivity> activities = data.subList(0, Math.min(databaseItemLimit, data.size()));
LoganSquare.serialize(activities, new FileOutputStream(file), ParcelableActivity.class);
} catch (final IOException e) {
// Ignore
}
}
}

View File

@ -55,12 +55,13 @@ public abstract class TwitterAPIStatusesLoader extends ParcelableStatusesLoader
private final Context mContext;
private final long mAccountId;
private final long mMaxId, mSinceId;
@Nullable
private final Object[] mSavedStatusesFileArgs;
private Comparator<ParcelableStatus> mComparator;
public TwitterAPIStatusesLoader(final Context context, final long accountId, final long sinceId,
final long maxId, final List<ParcelableStatus> data,
final String[] savedStatusesArgs, final int tabPosition, boolean fromUser) {
@Nullable final String[] savedStatusesArgs, final int tabPosition, boolean fromUser) {
super(context, data, tabPosition, fromUser);
mContext = context;
mAccountId = accountId;
@ -105,11 +106,14 @@ public abstract class TwitterAPIStatusesLoader extends ParcelableStatusesLoader
paging.setMaxId(mMaxId);
}
if (mSinceId > 0) {
paging.setSinceId(mSinceId - 1);
paging.setSinceId(mSinceId);
if (mMaxId <= 0) {
paging.setLatestResults(true);
}
}
statuses = new ArrayList<>();
truncated = Utils.truncateStatuses(getStatuses(twitter, paging), statuses, mSinceId);
if (!Utils.isOfficialTwitterInstance(context, twitter)) {
if (!TwitterAPIFactory.isOfficialTwitterInstance(context, twitter)) {
TwitterContentUtils.getStatusesWithQuoteData(twitter, statuses);
}
} catch (final TwitterException e) {
@ -167,6 +171,17 @@ public abstract class TwitterAPIStatusesLoader extends ParcelableStatusesLoader
mComparator = comparator;
}
public long getSinceId() {
return mSinceId;
}
public long getMaxId() {
return mMaxId;
}
public long getAccountId() {
return mAccountId;
}
@NonNull
protected abstract List<Status> getStatuses(@NonNull Twitter twitter, Paging paging) throws TwitterException;

View File

@ -32,8 +32,9 @@ import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.Utils;
import static org.mariotaku.twidere.util.Utils.getAccountId;
import static org.mariotaku.twidere.util.Utils.isFiltered;
public class UserTimelineLoader extends TwitterAPIStatusesLoader {
@ -49,7 +50,7 @@ public class UserTimelineLoader extends TwitterAPIStatusesLoader {
super(context, accountId, sinceId, maxId, data, savedStatusesArgs, tabPosition, fromUser);
mUserId = userId;
mUserScreenName = screenName;
mIsMyTimeline = userId > 0 ? accountId == userId : accountId == getAccountId(context, screenName);
mIsMyTimeline = userId > 0 ? accountId == userId : accountId == DataStoreUtils.getAccountId(context, screenName);
}
@NonNull

View File

@ -0,0 +1,24 @@
package org.mariotaku.twidere.model.util;
import android.support.annotation.NonNull;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.model.ParcelableStatus;
/**
* Created by mariotaku on 16/1/3.
*/
public class ParcelableStatusUtils {
public static void makeOriginalStatus(@NonNull ParcelableStatus status) {
if (!status.is_retweet) return;
status.id = status.retweet_id;
status.retweeted_by_user_id = -1;
status.retweeted_by_user_name = null;
status.retweeted_by_user_screen_name = null;
status.retweeted_by_user_profile_image = null;
status.retweet_timestamp = -1;
status.retweet_id = -1;
}
}

View File

@ -86,7 +86,6 @@ import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.model.DraftItem;
import org.mariotaku.twidere.model.DraftItemCursorIndices;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatusCursorIndices;
import org.mariotaku.twidere.model.StringLongPair;
import org.mariotaku.twidere.model.UnreadItem;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
@ -96,7 +95,6 @@ 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.Drafts;
import org.mariotaku.twidere.provider.TwidereDataStore.Mentions;
import org.mariotaku.twidere.provider.TwidereDataStore.NetworkUsages;
import org.mariotaku.twidere.provider.TwidereDataStore.Preferences;
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
@ -108,7 +106,6 @@ import org.mariotaku.twidere.receiver.NotificationReceiver;
import org.mariotaku.twidere.service.BackgroundOperationService;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.DatabaseQueryUtils;
import org.mariotaku.twidere.util.ImagePreloader;
import org.mariotaku.twidere.util.NotificationManagerWrapper;
import org.mariotaku.twidere.util.ParseUtils;
@ -146,7 +143,6 @@ import static org.mariotaku.twidere.util.DataStoreUtils.getTableId;
import static org.mariotaku.twidere.util.DataStoreUtils.getTableNameById;
import static org.mariotaku.twidere.util.Utils.clearAccountColor;
import static org.mariotaku.twidere.util.Utils.clearAccountName;
import static org.mariotaku.twidere.util.Utils.getAccountIds;
import static org.mariotaku.twidere.util.Utils.getNotificationUri;
import static org.mariotaku.twidere.util.Utils.isNotificationsSilent;
@ -1215,7 +1211,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
switch (tableId) {
case TABLE_ID_STATUSES: {
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
getAccountIds(context));
DataStoreUtils.getAccountIds(context));
for (final AccountPreferences pref : prefs) {
if (!pref.isHomeTimelineNotificationEnabled()) continue;
showTimelineNotification(pref, getPositionTag(TAB_TYPE_HOME_TIMELINE, pref.getAccountId()));
@ -1225,7 +1221,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
}
case TABLE_ID_ACTIVITIES_ABOUT_ME: {
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
getAccountIds(context));
DataStoreUtils.getAccountIds(context));
final boolean combined = mPreferences.getBoolean(KEY_COMBINED_NOTIFICATIONS);
for (final AccountPreferences pref : prefs) {
if (!pref.isInteractionsNotificationEnabled()) continue;
@ -1236,7 +1232,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
}
case TABLE_ID_DIRECT_MESSAGES_INBOX: {
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
getAccountIds(context));
DataStoreUtils.getAccountIds(context));
for (final AccountPreferences pref : prefs) {
if (!pref.isDirectMessagesNotificationEnabled()) continue;
final StringLongPair[] pairs = mReadStateManager.getPositionPairs(TAB_TYPE_DIRECT_MESSAGES);
@ -1443,7 +1439,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
final int usersCount = userCursor.getCount();
final int messagesCount = messageCursor.getCount();
if (messagesCount == 0 || usersCount == 0) return;
final String accountName = Utils.getAccountName(context, accountId);
final String accountName = DataStoreUtils.getAccountName(context, accountId);
final String accountScreenName = DataStoreUtils.getAccountScreenName(context, accountId);
final int idxMessageText = messageCursor.getColumnIndex(DirectMessages.TEXT_UNESCAPED),
idxMessageTimestamp = messageCursor.getColumnIndex(DirectMessages.MESSAGE_TIMESTAMP),

View File

@ -31,12 +31,12 @@ import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.util.ConnectivityUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.net.NetworkUsageUtils;
import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.UploadLogsTask;
import edu.tsinghua.hotmobi.model.NetworkEvent;
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
import static org.mariotaku.twidere.util.Utils.startRefreshServiceIfNeeded;
@ -64,7 +64,7 @@ public class ConnectivityStateReceiver extends BroadcastReceiver implements Cons
final int networkType = ConnectivityUtils.getActiveNetworkType(context.getApplicationContext());
NetworkUsageUtils.setNetworkType(networkType);
final boolean isWifi = networkType == ConnectivityManager.TYPE_WIFI;
final boolean isCharging = SpiceProfilingUtil.isCharging(context.getApplicationContext());
final boolean isCharging = Utils.isCharging(context.getApplicationContext());
if (isWifi && isCharging) {
final long currentTime = System.currentTimeMillis();
final long lastSuccessfulTime = HotMobiLogger.getLastUploadTime(context);

View File

@ -49,7 +49,6 @@ import javax.inject.Inject;
import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.model.ScreenEvent;
import static org.mariotaku.twidere.util.Utils.getAccountIds;
import static org.mariotaku.twidere.util.Utils.getDefaultAccountId;
import static org.mariotaku.twidere.util.Utils.hasAutoRefreshAccounts;
import static org.mariotaku.twidere.util.Utils.isBatteryOkay;
@ -84,7 +83,7 @@ public class RefreshService extends Service implements Constants {
} else if (BROADCAST_RESCHEDULE_TRENDS_REFRESHING.equals(action)) {
rescheduleTrendsRefreshing();
} else if (isAutoRefreshAllowed()) {
final long[] accountIds = getAccountIds(context);
final long[] accountIds = DataStoreUtils.getAccountIds(context);
final AccountPreferences[] accountPrefs = AccountPreferences.getAccountPreferences(context, accountIds);
if (BROADCAST_REFRESH_HOME_TIMELINE.equals(action)) {
final long[] refreshIds = getRefreshableIds(accountPrefs, new HomeRefreshableFilter());

View File

@ -72,7 +72,8 @@ public abstract class GetStatusesTask extends ManagedAsyncTask<Object, TwitterWr
return sinceIds != null && sinceIds.length == accountIds.length;
}
private void storeStatus(long accountId, List<org.mariotaku.twidere.api.twitter.model.Status> statuses, long maxId, boolean truncated, boolean notify) {
private void storeStatus(long accountId, List<org.mariotaku.twidere.api.twitter.model.Status> statuses,
long maxId, boolean notify, int loadItemLimit) {
if (statuses == null || statuses.isEmpty() || accountId <= 0) {
return;
}
@ -96,7 +97,8 @@ public abstract class GetStatusesTask extends ManagedAsyncTask<Object, TwitterWr
}
// Delete all rows conflicting before new data inserted.
final Expression accountWhere = Expression.equals(TwidereDataStore.Statuses.ACCOUNT_ID, accountId);
final Expression statusWhere = Expression.in(new Columns.Column(TwidereDataStore.Statuses.STATUS_ID), new RawItemArray(statusIds));
final Expression statusWhere = Expression.in(new Columns.Column(TwidereDataStore.Statuses.STATUS_ID),
new RawItemArray(statusIds));
final String countWhere = Expression.and(accountWhere, statusWhere).getSQL();
final String[] projection = {SQLFunctions.COUNT()};
final int rowsDeleted;
@ -119,8 +121,8 @@ public abstract class GetStatusesTask extends ManagedAsyncTask<Object, TwitterWr
// Insert a gap.
final boolean deletedOldGap = rowsDeleted > 0 && ArrayUtils.contains(statusIds, maxId);
final boolean noRowsDeleted = rowsDeleted == 0;
final boolean insertGap = minId > 0 && (noRowsDeleted || deletedOldGap) && !truncated
&& !noItemsBefore && statuses.size() > 1;
final boolean insertGap = minId > 0 && (noRowsDeleted || deletedOldGap) && !noItemsBefore
&& statuses.size() >= loadItemLimit;
if (insertGap && minIdx != -1) {
values[minIdx].put(TwidereDataStore.Statuses.IS_GAP, true);
}
@ -181,10 +183,9 @@ public abstract class GetStatusesTask extends ManagedAsyncTask<Object, TwitterWr
} else {
sinceId = -1;
}
final List<org.mariotaku.twidere.api.twitter.model.Status> statuses = new ArrayList<>();
final boolean truncated = Utils.truncateStatuses(getStatuses(twitter, paging), statuses, sinceId);
final List<org.mariotaku.twidere.api.twitter.model.Status> statuses = getStatuses(twitter, paging);
TwitterContentUtils.getStatusesWithQuoteData(twitter, statuses);
storeStatus(accountId, statuses, maxId, truncated, true);
storeStatus(accountId, statuses, maxId, true, loadItemLimit);
publishProgress(new TwitterWrapper.StatusListResponse(accountId, statuses));
} catch (final TwitterException e) {
Log.w(LOGTAG, e);

View File

@ -116,7 +116,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
private final UserColorNameManager mUserColorNameManager;
private final ReadStateManager mReadStateManager;
private int mGetHomeTimelineTaskId, mGetMentionsTaskId;
private int mGetReceivedDirectMessagesTaskId, mGetSentDirectMessagesTaskId;
private int mGetLocalTrendsTaskId;
@ -282,9 +281,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
public boolean getHomeTimelineAsync(final long[] accountIds, final long[] max_ids, final long[] since_ids) {
mAsyncTaskManager.cancel(mGetHomeTimelineTaskId);
final GetHomeTimelineTask task = new GetHomeTimelineTask(accountIds, max_ids, since_ids);
mGetHomeTimelineTaskId = mAsyncTaskManager.add(task, true);
final GetHomeTimelineTask task = new GetHomeTimelineTask(this, accountIds, max_ids, since_ids);
mAsyncTaskManager.add(task, true);
return true;
}
@ -544,7 +542,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
@Override
protected ResponseList<Activity> getActivities(long accountId, Twitter twitter, Paging paging) throws TwitterException {
if (Utils.isOfficialKeyAccount(getContext(), accountId)) {
if (TwitterAPIFactory.isOfficialKeyAccount(getContext(), accountId)) {
return twitter.getActivitiesAboutMe(paging);
}
final ResponseList<Activity> activities = new ResponseList<>();
@ -588,7 +586,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
public Object doLongOperation(Object o) throws InterruptedException {
for (long accountId : accountIds) {
Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, accountId, false);
if (Utils.isOfficialTwitterInstance(mContext, twitter)) continue;
if (TwitterAPIFactory.isOfficialTwitterInstance(mContext, twitter)) continue;
try {
twitter.setActivitiesAboutMeUnread(cursor);
} catch (TwitterException e) {
@ -2125,10 +2123,13 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
class GetHomeTimelineTask extends GetStatusesTask {
static class GetHomeTimelineTask extends GetStatusesTask {
public GetHomeTimelineTask(final long[] accountIds, final long[] maxIds, final long[] sinceIds) {
super(AsyncTwitterWrapper.this, accountIds, maxIds, sinceIds, TASK_TAG_GET_HOME_TIMELINE);
private AsyncTwitterWrapper twitterWrapper;
public GetHomeTimelineTask(AsyncTwitterWrapper asyncTwitterWrapper, final long[] accountIds, final long[] maxIds, final long[] sinceIds) {
super(asyncTwitterWrapper, accountIds, maxIds, sinceIds, TASK_TAG_GET_HOME_TIMELINE);
this.twitterWrapper = asyncTwitterWrapper;
}
@Override
@ -2152,13 +2153,12 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
@Override
protected void onPostExecute(final List<StatusListResponse> result) {
super.onPostExecute(result);
mGetHomeTimelineTaskId = -1;
}
@Override
protected void onPreExecute() {
final Intent intent = new Intent(BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING);
mContext.sendBroadcast(intent);
twitterWrapper.getContext().sendBroadcast(intent);
super.onPreExecute();
}

View File

@ -23,10 +23,12 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.util.LongSparseArray;
import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.sqliteqb.library.Columns;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.sqliteqb.library.RawItemArray;
@ -70,6 +72,7 @@ import static android.text.TextUtils.isEmpty;
*/
public class DataStoreUtils implements Constants {
static final UriMatcher CONTENT_PROVIDER_URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static LongSparseArray<Integer> sAccountColors = new LongSparseArray<>();
static {
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Accounts.CONTENT_PATH,
@ -714,4 +717,122 @@ public class DataStoreUtils implements Constants {
}
return filterExpression;
}
public static int getAccountColor(final Context context, final long accountId) {
if (context == null) return Color.TRANSPARENT;
final Integer cached = sAccountColors.get(accountId);
if (cached != null) return cached;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.COLOR}, Expression.equals(Accounts.ACCOUNT_ID, accountId).getSQL(),
null, null);
if (cur == null) return Color.TRANSPARENT;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) {
final int color = cur.getInt(0);
sAccountColors.put(accountId, color);
return color;
}
return Color.TRANSPARENT;
} finally {
cur.close();
}
}
public static int[] getAccountColors(final Context context, final long[] accountIds) {
if (context == null || accountIds == null) return new int[0];
final String[] cols = new String[]{Accounts.ACCOUNT_ID, Accounts.COLOR};
final String where = Expression.in(new Columns.Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL();
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where,
null, null);
if (cur == null) return new int[0];
try {
final int[] colors = new int[cur.getCount()];
for (int i = 0, j = cur.getCount(); i < j; i++) {
cur.moveToPosition(i);
colors[ArrayUtils.indexOf(accountIds, cur.getLong(0))] = cur.getInt(1);
}
return colors;
} finally {
cur.close();
}
}
public static String getAccountDisplayName(final Context context, final long accountId, final boolean nameFirst) {
final String name;
if (nameFirst) {
name = getAccountName(context, accountId);
} else {
name = String.format("@%s", getAccountScreenName(context, accountId));
}
return name;
}
public static long getAccountId(final Context context, final String screenName) {
if (context == null || isEmpty(screenName)) return -1;
final Cursor cur = ContentResolverUtils
.query(context.getContentResolver(), Accounts.CONTENT_URI, new String[]{Accounts.ACCOUNT_ID},
Expression.equalsArgs(Accounts.SCREEN_NAME).getSQL(), new String[]{screenName}, null);
if (cur == null) return -1;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) return cur.getLong(0);
return -1;
} finally {
cur.close();
}
}
@NonNull
public static long[] getAccountIds(final Context context) {
if (context == null) return new long[0];
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.ACCOUNT_ID}, null, null, null);
if (cur == null) return new long[0];
try {
cur.moveToFirst();
final long[] ids = new long[cur.getCount()];
int i = 0;
while (!cur.isAfterLast()) {
ids[i++] = cur.getLong(0);
cur.moveToNext();
}
return ids;
} finally {
cur.close();
}
}
public static boolean hasAccount(final Context context) {
if (context == null) return false;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{SQLFunctions.COUNT()}, null, null, null);
try {
cur.moveToFirst();
return cur.getInt(0) > 0;
} finally {
cur.close();
}
}
public static String getAccountName(final Context context, final long accountId) {
if (context == null) return null;
final String cached = sAccountNames.get(accountId);
if (!isEmpty(cached)) return cached;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.NAME}, Accounts.ACCOUNT_ID + " = " + accountId, null, null);
if (cur == null) return null;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) {
final String name = cur.getString(0);
sAccountNames.put(accountId, name);
return name;
}
return null;
} finally {
cur.close();
}
}
public static String[] getAccountNames(final Context context) {
return getAccountScreenNames(context, null);
}
}

View File

@ -0,0 +1,33 @@
package org.mariotaku.twidere.util;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.lang.reflect.Field;
/**
* Created by mariotaku on 16/1/3.
*/
public class ParcelUtils {
@Nullable
public static <T extends Parcelable> T clone(@Nullable T object) {
if (object == null) return null;
final Parcel parcel = Parcel.obtain();
try {
object.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
final Field creatorField = object.getClass().getDeclaredField("CREATOR");
//noinspection unchecked
final Parcelable.Creator<T> creator = (Parcelable.Creator<T>) creatorField.get(null);
return creator.createFromParcel(parcel);
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError("Missing CREATOR field");
} catch (IllegalAccessException e) {
throw new IllegalAccessError("Can't access CREATOR field");
} finally {
parcel.recycle();
}
}
}

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;
import android.support.annotation.NonNull;
@ -25,6 +26,7 @@ import org.mariotaku.restfu.HttpRequestFactory;
import org.mariotaku.restfu.Pair;
import org.mariotaku.restfu.RequestInfoFactory;
import org.mariotaku.restfu.RestAPIFactory;
import org.mariotaku.restfu.RestClient;
import org.mariotaku.restfu.RestMethodInfo;
import org.mariotaku.restfu.RestRequestInfo;
import org.mariotaku.restfu.annotation.RestMethod;
@ -38,6 +40,7 @@ import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.restfu.http.mime.StringTypedData;
import org.mariotaku.restfu.http.mime.TypedData;
import org.mariotaku.restfu.okhttp.OkHttpRestClient;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.api.twitter.Twitter;
@ -50,11 +53,13 @@ import org.mariotaku.twidere.api.twitter.auth.BasicAuthorization;
import org.mariotaku.twidere.api.twitter.auth.EmptyAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint;
import org.mariotaku.twidere.api.twitter.auth.OAuthSupport;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
import org.mariotaku.twidere.api.twitter.util.TwitterConverter;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.RequestType;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.util.dagger.DependencyHolder;
import org.mariotaku.twidere.util.net.InetAddressUtils;
import org.mariotaku.twidere.util.net.NetworkUsageUtils;
@ -483,6 +488,36 @@ public class TwitterAPIFactory implements TwidereConstants {
return ('A' <= codePoint && codePoint <= 'Z') || ('a' <= codePoint && codePoint <= 'z') || '0' <= codePoint && codePoint <= '9';
}
public static boolean isOfficialKeyAccount(final Context context, final long accountId) {
return getOfficialKeyType(context, accountId) != ConsumerKeyType.UNKNOWN;
}
@NonNull
public static ConsumerKeyType getOfficialKeyType(final Context context, final long accountId) {
if (context == null) return ConsumerKeyType.UNKNOWN;
final String[] projection = {TwidereDataStore.Accounts.CONSUMER_KEY, TwidereDataStore.Accounts.CONSUMER_SECRET};
final String selection = Expression.equals(TwidereDataStore.Accounts.ACCOUNT_ID, accountId).getSQL();
final Cursor c = context.getContentResolver().query(TwidereDataStore.Accounts.CONTENT_URI, projection, selection, null, null);
//noinspection TryFinallyCanBeTryWithResources
try {
if (c.moveToPosition(0))
return TwitterContentUtils.getOfficialKeyType(context, c.getString(0), c.getString(1));
} finally {
c.close();
}
return ConsumerKeyType.UNKNOWN;
}
public static boolean isOfficialTwitterInstance(final Context context, final Twitter twitter) {
if (context == null || twitter == null) return false;
final RestClient restClient = RestAPIFactory.getRestClient(twitter);
final Authorization auth = restClient.getAuthorization();
if (!(auth instanceof OAuthSupport)) return false;
final String consumerKey = ((OAuthSupport) auth).getConsumerKey();
final String consumerSecret = ((OAuthSupport) auth).getConsumerSecret();
return TwitterContentUtils.isOfficialKey(context, consumerKey, consumerSecret);
}
public static class Options {
final HashMap<String, String> extras = new HashMap<>();

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
@ -74,7 +75,6 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.ListFragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.LongSparseArray;
import android.support.v4.util.Pair;
import android.support.v4.view.ActionProvider;
import android.support.v4.view.GravityCompat;
@ -116,16 +116,12 @@ import com.bluelinelabs.logansquare.LoganSquare;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.json.JSONException;
import org.mariotaku.restfu.RestAPIFactory;
import org.mariotaku.restfu.RestClient;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.sqliteqb.library.AllColumns;
import org.mariotaku.sqliteqb.library.Columns;
import org.mariotaku.sqliteqb.library.Columns.Column;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.sqliteqb.library.OrderBy;
import org.mariotaku.sqliteqb.library.RawItemArray;
import org.mariotaku.sqliteqb.library.SQLFunctions;
import org.mariotaku.sqliteqb.library.Selectable;
import org.mariotaku.sqliteqb.library.Tables;
import org.mariotaku.sqliteqb.library.query.SQLSelectQuery;
@ -140,7 +136,6 @@ import org.mariotaku.twidere.adapter.iface.IBaseAdapter;
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.auth.OAuthSupport;
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.GeoLocation;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;
@ -186,7 +181,6 @@ import org.mariotaku.twidere.graphic.ActionIconDrawable;
import org.mariotaku.twidere.graphic.PaddingDrawable;
import org.mariotaku.twidere.menu.SupportStatusShareProvider;
import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableCredentialsCursorIndices;
@ -303,8 +297,6 @@ public final class Utils implements Constants {
}
private static LongSparseArray<Integer> sAccountColors = new LongSparseArray<>();
private Utils() {
throw new AssertionError("You are trying to create an instance for this utility class!");
}
@ -457,7 +449,7 @@ public final class Utils implements Constants {
final int itemLimit = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).getInt(
KEY_DATABASE_ITEM_LIMIT, DEFAULT_DATABASE_ITEM_LIMIT);
for (final long accountId : getAccountIds(context)) {
for (final long accountId : DataStoreUtils.getAccountIds(context)) {
// Clean statuses.
for (final Uri uri : STATUSES_URIS) {
if (CachedStatuses.CONTENT_URI.equals(uri)) {
@ -500,7 +492,7 @@ public final class Utils implements Constants {
}
public static void clearAccountColor() {
sAccountColors.clear();
DataStoreUtils.sAccountColors.clear();
}
public static void clearAccountName() {
@ -909,7 +901,7 @@ public final class Utils implements Constants {
} else {
final String paramAccountName = uri.getQueryParameter(QUERY_PARAM_ACCOUNT_NAME);
if (paramAccountName != null) {
args.putLong(EXTRA_ACCOUNT_ID, getAccountId(context, paramAccountName));
args.putLong(EXTRA_ACCOUNT_ID, DataStoreUtils.getAccountId(context, paramAccountName));
} else {
final long accountId = getDefaultAccountId(context);
if (isMyAccount(context, accountId)) {
@ -1100,124 +1092,6 @@ public final class Utils implements Constants {
return DateUtils.formatDateTime(context, timestamp, format_flags);
}
public static int getAccountColor(final Context context, final long accountId) {
if (context == null) return Color.TRANSPARENT;
final Integer cached = sAccountColors.get(accountId);
if (cached != null) return cached;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.COLOR}, Expression.equals(Accounts.ACCOUNT_ID, accountId).getSQL(),
null, null);
if (cur == null) return Color.TRANSPARENT;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) {
final int color = cur.getInt(0);
sAccountColors.put(accountId, color);
return color;
}
return Color.TRANSPARENT;
} finally {
cur.close();
}
}
public static int[] getAccountColors(final Context context, final long[] accountIds) {
if (context == null || accountIds == null) return new int[0];
final String[] cols = new String[]{Accounts.ACCOUNT_ID, Accounts.COLOR};
final String where = Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL();
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where,
null, null);
if (cur == null) return new int[0];
try {
final int[] colors = new int[cur.getCount()];
for (int i = 0, j = cur.getCount(); i < j; i++) {
cur.moveToPosition(i);
colors[ArrayUtils.indexOf(accountIds, cur.getLong(0))] = cur.getInt(1);
}
return colors;
} finally {
cur.close();
}
}
public static String getAccountDisplayName(final Context context, final long accountId, final boolean nameFirst) {
final String name;
if (nameFirst) {
name = getAccountName(context, accountId);
} else {
name = String.format("@%s", DataStoreUtils.getAccountScreenName(context, accountId));
}
return name;
}
public static long getAccountId(final Context context, final String screenName) {
if (context == null || isEmpty(screenName)) return -1;
final Cursor cur = ContentResolverUtils
.query(context.getContentResolver(), Accounts.CONTENT_URI, new String[]{Accounts.ACCOUNT_ID},
Expression.equalsArgs(Accounts.SCREEN_NAME).getSQL(), new String[]{screenName}, null);
if (cur == null) return -1;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) return cur.getLong(0);
return -1;
} finally {
cur.close();
}
}
@NonNull
public static long[] getAccountIds(final Context context) {
if (context == null) return new long[0];
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.ACCOUNT_ID}, null, null, null);
if (cur == null) return new long[0];
try {
cur.moveToFirst();
final long[] ids = new long[cur.getCount()];
int i = 0;
while (!cur.isAfterLast()) {
ids[i++] = cur.getLong(0);
cur.moveToNext();
}
return ids;
} finally {
cur.close();
}
}
public static boolean hasAccount(final Context context) {
if (context == null) return false;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{SQLFunctions.COUNT()}, null, null, null);
try {
cur.moveToFirst();
return cur.getInt(0) > 0;
} finally {
cur.close();
}
}
public static String getAccountName(final Context context, final long accountId) {
if (context == null) return null;
final String cached = DataStoreUtils.sAccountNames.get(accountId);
if (!isEmpty(cached)) return cached;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
new String[]{Accounts.NAME}, Accounts.ACCOUNT_ID + " = " + accountId, null, null);
if (cur == null) return null;
try {
if (cur.getCount() > 0 && cur.moveToFirst()) {
final String name = cur.getString(0);
DataStoreUtils.sAccountNames.put(accountId, name);
return name;
}
return null;
} finally {
cur.close();
}
}
public static String[] getAccountNames(final Context context) {
return DataStoreUtils.getAccountScreenNames(context, null);
}
public static int getAccountNotificationId(final int notificationType, final long accountId) {
return Arrays.hashCode(new long[]{notificationType, accountId});
}
@ -1385,7 +1259,7 @@ public final class Utils implements Constants {
if (context == null) return -1;
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final long accountId = prefs.getLong(KEY_DEFAULT_ACCOUNT_ID, -1);
final long[] accountIds = Utils.getAccountIds(context);
final long[] accountIds = DataStoreUtils.getAccountIds(context);
if (accountIds.length > 0 && !ArrayUtils.contains(accountIds, accountId) && accountIds.length > 0) {
/* TODO: this is just a quick fix */
return accountIds[0];
@ -1816,7 +1690,7 @@ public final class Utils implements Constants {
}
public static boolean hasAutoRefreshAccounts(final Context context) {
final long[] accountIds = getAccountIds(context);
final long[] accountIds = DataStoreUtils.getAccountIds(context);
return !ArrayUtils.isEmpty(AccountPreferences.getAutoRefreshEnabledAccountIds(context, accountIds));
}
@ -1832,7 +1706,7 @@ public final class Utils implements Constants {
final int id_idx = cur.getColumnIndex(Accounts.ACCOUNT_ID), color_idx = cur.getColumnIndex(Accounts.COLOR);
cur.moveToFirst();
while (!cur.isAfterLast()) {
sAccountColors.put(cur.getLong(id_idx), cur.getInt(color_idx));
DataStoreUtils.sAccountColors.put(cur.getLong(id_idx), cur.getInt(color_idx));
cur.moveToNext();
}
cur.close();
@ -1980,36 +1854,6 @@ public final class Utils implements Constants {
return prefs.getBoolean("silent_notifications_at_" + now.get(Calendar.HOUR_OF_DAY), false);
}
public static boolean isOfficialKeyAccount(final Context context, final long accountId) {
return getOfficialKeyType(context, accountId) != ConsumerKeyType.UNKNOWN;
}
@NonNull
public static ConsumerKeyType getOfficialKeyType(final Context context, final long accountId) {
if (context == null) return ConsumerKeyType.UNKNOWN;
final String[] projection = {Accounts.CONSUMER_KEY, Accounts.CONSUMER_SECRET};
final String selection = Expression.equals(Accounts.ACCOUNT_ID, accountId).getSQL();
final Cursor c = context.getContentResolver().query(Accounts.CONTENT_URI, projection, selection, null, null);
//noinspection TryFinallyCanBeTryWithResources
try {
if (c.moveToPosition(0))
return TwitterContentUtils.getOfficialKeyType(context, c.getString(0), c.getString(1));
} finally {
c.close();
}
return ConsumerKeyType.UNKNOWN;
}
public static boolean isOfficialTwitterInstance(final Context context, final Twitter twitter) {
if (context == null || twitter == null) return false;
final RestClient restClient = RestAPIFactory.getRestClient(twitter);
final Authorization auth = restClient.getAuthorization();
if (!(auth instanceof OAuthSupport)) return false;
final String consumerKey = ((OAuthSupport) auth).getConsumerKey();
final String consumerSecret = ((OAuthSupport) auth).getConsumerSecret();
return TwitterContentUtils.isOfficialKey(context, consumerKey, consumerSecret);
}
public static boolean isRedirected(final int code) {
return code == 301 || code == 302 || code == 307;
}
@ -2025,7 +1869,7 @@ public final class Utils implements Constants {
public static boolean isUserLoggedIn(final Context context, final long accountId) {
if (context == null) return false;
final long[] ids = getAccountIds(context);
final long[] ids = DataStoreUtils.getAccountIds(context);
if (ids == null) return false;
for (final long id : ids) {
if (id == accountId) return true;
@ -3056,18 +2900,6 @@ public final class Utils implements Constants {
return in.size() != out.size();
}
public static boolean truncateActivities(final List<org.mariotaku.twidere.api.twitter.model.Activity> in, final List<org.mariotaku.twidere.api.twitter.model.Activity> out,
final long sinceId) {
if (in == null) return false;
for (final org.mariotaku.twidere.api.twitter.model.Activity status : in) {
if (sinceId > 0 && status.getMaxPosition() <= sinceId) {
continue;
}
out.add(status);
}
return in.size() != out.size();
}
public static void updateRelationship(Context context, Relationship relationship, long accountId) {
final ContentResolver resolver = context.getContentResolver();
final ContentValues values = ContentValuesCreator.createCachedRelationship(relationship, accountId);
@ -3403,6 +3235,17 @@ public final class Utils implements Constants {
return result;
}
@SuppressLint("InlinedApi")
public static boolean isCharging(final Context context) {
if (context == null) return false;
final Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (intent == null) return false;
final int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB
|| plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
static class UtilsL {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)

View File

@ -31,9 +31,9 @@ import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.MessageEntriesAdapter;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.ShortTimeView;
import org.mariotaku.twidere.view.iface.IColorLabelView;
@ -88,7 +88,7 @@ public class MessageEntryViewHolder extends ViewHolder implements OnClickListene
screenNameView.setTypeface(null, isUnread && !isOutgoing ? Typeface.BOLD : Typeface.NORMAL);
textView.setTypeface(null, isUnread && !isOutgoing ? Typeface.BOLD : Typeface.NORMAL);
if (adapter.shouldShowAccountsColor()) {
content.drawEnd(Utils.getAccountColor(context, accountId));
content.drawEnd(DataStoreUtils.getAccountColor(context, accountId));
} else {
content.drawEnd();
}

View File

@ -22,6 +22,7 @@ import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.HtmlSpanBuilder;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.MediaLoadingHandler;
@ -236,7 +237,7 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi
}
if (adapter.shouldShowAccountsColor()) {
itemContent.drawEnd(Utils.getAccountColor(context, status.account_id));
itemContent.drawEnd(DataStoreUtils.getAccountColor(context, status.account_id));
} else {
itemContent.drawEnd();
}