org.mariotaku.twidere.util.AsyncTwitterWrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.mariotaku.twidere.util.AsyncTwitterWrapper.java

Source

/*
 *            Twidere - Twitter client for Android
 * 
 * Copyright (C) 2012 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 static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.provider.TweetStore.STATUSES_URIS;
import static org.mariotaku.twidere.util.Utils.appendQueryParameters;
import static org.mariotaku.twidere.util.Utils.getAccountScreenName;
import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds;
import static org.mariotaku.twidere.util.Utils.getAllStatusesIds;
import static org.mariotaku.twidere.util.Utils.getImagePathFromUri;
import static org.mariotaku.twidere.util.Utils.getImageUploadStatus;
import static org.mariotaku.twidere.util.Utils.getNewestMessageIdsFromDatabase;
import static org.mariotaku.twidere.util.Utils.getNewestStatusIdsFromDatabase;
import static org.mariotaku.twidere.util.Utils.getStatusIdsInDatabase;
import static org.mariotaku.twidere.util.Utils.getTwitterInstance;
import static org.mariotaku.twidere.util.Utils.makeDirectMessageContentValues;
import static org.mariotaku.twidere.util.Utils.makeStatusContentValues;
import static org.mariotaku.twidere.util.Utils.makeTrendsContentValues;
import static org.mariotaku.twidere.util.Utils.parseString;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.mariotaku.twidere.R;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.model.ListResponse;
import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.provider.TweetStore;
import org.mariotaku.twidere.provider.TweetStore.CachedHashtags;
import org.mariotaku.twidere.provider.TweetStore.CachedTrends;
import org.mariotaku.twidere.provider.TweetStore.CachedUsers;
import org.mariotaku.twidere.provider.TweetStore.DirectMessages;
import org.mariotaku.twidere.provider.TweetStore.Drafts;
import org.mariotaku.twidere.provider.TweetStore.Mentions;
import org.mariotaku.twidere.provider.TweetStore.Statuses;

import twitter4j.DirectMessage;
import twitter4j.Paging;
import twitter4j.ResponseList;
import twitter4j.StatusUpdate;
import twitter4j.Trends;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;
import twitter4j.UserList;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;

import com.twitter.Extractor;
import com.twitter.Validator;

import edu.ucdavis.earlybird.ProfilingUtil;

public class AsyncTwitterWrapper extends TwitterWrapper {

    private static AsyncTwitterWrapper sInstance;

    private final Context mContext;
    private final AsyncTaskManager mAsyncTaskManager;
    private final SharedPreferences mPreferences;
    private final NotificationManager mNotificationManager;
    private final ContentResolver mResolver;
    private final Resources mResources;

    private final boolean large_profile_image;

    private int mGetHomeTimelineTaskId, mGetMentionsTaskId;
    private int mGetReceivedDirectMessagesTaskId, mGetSentDirectMessagesTaskId;
    private int mGetLocalTrendsTaskId;

    public AsyncTwitterWrapper(final Context context) {
        mContext = context;
        mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        mAsyncTaskManager = TwidereApplication.getInstance(context).getAsyncTaskManager();
        mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
        mResolver = context.getContentResolver();
        mResources = context.getResources();
        large_profile_image = context.getResources().getBoolean(R.bool.hires_profile_image);
    }

    public int addUserListMember(final long account_id, final int list_id, final long user_id,
            final String screen_name) {
        final AddUserListMemberTask task = new AddUserListMemberTask(account_id, list_id, user_id, screen_name);
        return mAsyncTaskManager.add(task, true);
    }

    public void clearNotification(final int id) {
        final Uri uri = TweetStore.CONTENT_URI_NOTOFICATIONS.buildUpon().appendPath(String.valueOf(id)).build();
        mResolver.delete(uri, null, null);
    }

    public int createBlockAsync(final long account_id, final long user_id) {
        final CreateBlockTask task = new CreateBlockTask(account_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int createFavoriteAsync(final long account_id, final long status_id) {
        final CreateFavoriteTask task = new CreateFavoriteTask(account_id, status_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int createFriendship(final long account_id, final long user_id) {
        final CreateFriendshipTask task = new CreateFriendshipTask(account_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int createMultiBlock(final long account_id, final long[] user_ids) {
        final CreateMultiBlockTask task = new CreateMultiBlockTask(account_id, user_ids);
        return mAsyncTaskManager.add(task, true);
    }

    public int createUserList(final long account_id, final String list_name, final boolean is_public,
            final String description) {
        final CreateUserListTask task = new CreateUserListTask(account_id, list_name, is_public, description);
        return mAsyncTaskManager.add(task, true);
    }

    public int createUserListSubscription(final long account_id, final int list_id) {
        final CreateUserListSubscriptionTask task = new CreateUserListSubscriptionTask(account_id, list_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int deleteUserListMember(final long account_id, final int list_id, final long user_id) {
        final DeleteUserListMemberTask task = new DeleteUserListMemberTask(account_id, list_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyBlock(final long account_id, final long user_id) {
        final DestroyBlockTask task = new DestroyBlockTask(account_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyDirectMessage(final long account_id, final long message_id) {
        final DestroyDirectMessageTask task = new DestroyDirectMessageTask(account_id, message_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyFavorite(final long account_id, final long status_id) {
        final DestroyFavoriteTask task = new DestroyFavoriteTask(account_id, status_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyFriendship(final long account_id, final long user_id) {
        final DestroyFriendshipTask task = new DestroyFriendshipTask(account_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyStatus(final long account_id, final long status_id) {
        final DestroyStatusTask task = new DestroyStatusTask(account_id, status_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyUserList(final long account_id, final int list_id) {
        final DestroyUserListTask task = new DestroyUserListTask(account_id, list_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int destroyUserListSubscription(final long account_id, final int list_id) {
        final DestroyUserListSubscriptionTask task = new DestroyUserListSubscriptionTask(account_id, list_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int getHomeTimeline(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
        mAsyncTaskManager.cancel(mGetHomeTimelineTaskId);
        final GetHomeTimelineTask task = new GetHomeTimelineTask(account_ids, max_ids, since_ids);
        return mGetHomeTimelineTaskId = mAsyncTaskManager.add(task, true);
    }

    public int getLocalTrends(final long account_id, final int woeid) {
        mAsyncTaskManager.cancel(mGetLocalTrendsTaskId);
        final GetLocalTrendsTask task = new GetLocalTrendsTask(account_id, woeid);
        return mGetLocalTrendsTaskId = mAsyncTaskManager.add(task, true);
    }

    public int getMentions(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
        mAsyncTaskManager.cancel(mGetMentionsTaskId);
        final GetMentionsTask task = new GetMentionsTask(account_ids, max_ids, since_ids);
        return mGetMentionsTaskId = mAsyncTaskManager.add(task, true);
    }

    public int getReceivedDirectMessages(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
        mAsyncTaskManager.cancel(mGetReceivedDirectMessagesTaskId);
        final GetReceivedDirectMessagesTask task = new GetReceivedDirectMessagesTask(account_ids, max_ids,
                since_ids);
        return mGetReceivedDirectMessagesTaskId = mAsyncTaskManager.add(task, true);
    }

    public int getSentDirectMessages(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
        mAsyncTaskManager.cancel(mGetSentDirectMessagesTaskId);
        final GetSentDirectMessagesTask task = new GetSentDirectMessagesTask(account_ids, max_ids, since_ids);
        return mGetSentDirectMessagesTaskId = mAsyncTaskManager.add(task, true);
    }

    public boolean hasActivatedTask() {
        return mAsyncTaskManager.hasRunningTask();
    }

    public boolean isHomeTimelineRefreshing() {
        return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_HOME_TIMELINE)
                || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_HOME_TIMELINE);
    }

    public boolean isLocalTrendsRefreshing() {
        return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_TRENDS)
                || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_TRENDS);
    }

    public boolean isMentionsRefreshing() {
        return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_MENTIONS)
                || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_MENTIONS);
    }

    public boolean isReceivedDirectMessagesRefreshing() {
        return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_RECEIVED_DIRECT_MESSAGES)
                || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_RECEIVED_DIRECT_MESSAGES);
    }

    public boolean isSentDirectMessagesRefreshing() {
        return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_SENT_DIRECT_MESSAGES)
                || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_SENT_DIRECT_MESSAGES);
    }

    public int refreshAll() {
        final long[] account_ids = getActivatedAccountIds(mContext);
        if (mPreferences.getBoolean(PREFERENCE_KEY_HOME_REFRESH_MENTIONS, false)) {
            final long[] since_ids = getNewestStatusIdsFromDatabase(mContext, Mentions.CONTENT_URI);
            getMentions(account_ids, null, since_ids);
        }
        if (mPreferences.getBoolean(PREFERENCE_KEY_HOME_REFRESH_DIRECT_MESSAGES, false)) {
            final long[] since_ids = getNewestMessageIdsFromDatabase(mContext, DirectMessages.Inbox.CONTENT_URI);
            getReceivedDirectMessages(account_ids, null, since_ids);
            getSentDirectMessages(account_ids, null, null);
        }
        final long[] since_ids = getNewestStatusIdsFromDatabase(mContext, Statuses.CONTENT_URI);
        return getHomeTimeline(account_ids, null, since_ids);
    }

    public int reportMultiSpam(final long account_id, final long[] user_ids) {
        final ReportMultiSpamTask task = new ReportMultiSpamTask(account_id, user_ids);
        return mAsyncTaskManager.add(task, true);
    }

    public int reportSpam(final long account_id, final long user_id) {
        final ReportSpamTask task = new ReportSpamTask(account_id, user_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int retweetStatus(final long account_id, final long status_id) {
        final RetweetStatusTask task = new RetweetStatusTask(account_id, status_id);
        return mAsyncTaskManager.add(task, true);
    }

    public int sendDirectMessage(final long account_id, final String screen_name, final long user_id,
            final String message) {
        final SendDirectMessageTask task = new SendDirectMessageTask(account_id, screen_name, user_id, message);
        return mAsyncTaskManager.add(task, true);
    }

    public int updateProfile(final long account_id, final String name, final String url, final String location,
            final String description) {
        final UpdateProfileTask task = new UpdateProfileTask(mContext, mAsyncTaskManager, account_id, name, url,
                location, description);
        return mAsyncTaskManager.add(task, true);
    }

    public int updateProfileBannerImage(final long account_id, final Uri image_uri, final boolean delete_image) {
        final UpdateProfileBannerImageTask task = new UpdateProfileBannerImageTask(mContext, mAsyncTaskManager,
                account_id, image_uri, delete_image);
        return mAsyncTaskManager.add(task, true);
    }

    public int updateProfileImage(final long account_id, final Uri image_uri, final boolean delete_image) {
        final UpdateProfileImageTask task = new UpdateProfileImageTask(mContext, mAsyncTaskManager, account_id,
                image_uri, delete_image);
        return mAsyncTaskManager.add(task, true);
    }

    public int updateStatus(final long[] account_ids, final String content, final ParcelableLocation location,
            final Uri image_uri, final long in_reply_to, final boolean is_possibly_sensitive,
            final boolean delete_image) {
        final UpdateStatusTask task = new UpdateStatusTask(account_ids, content, location, image_uri, in_reply_to,
                is_possibly_sensitive, delete_image);
        return mAsyncTaskManager.add(task, true);
    }

    public int updateUserListDetails(final long account_id, final int list_id, final boolean is_public,
            final String name, final String description) {
        final UpdateUserListDetailsTask task = new UpdateUserListDetailsTask(account_id, list_id, is_public, name,
                description);
        return mAsyncTaskManager.add(task, true);
    }

    private Notification buildNotification(final String title, final String message, final int icon,
            final Intent content_intent, final Intent delete_intent) {
        final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
        builder.setTicker(message);
        builder.setContentTitle(title);
        builder.setContentText(message);
        builder.setAutoCancel(true);
        builder.setWhen(System.currentTimeMillis());
        builder.setSmallIcon(icon);
        if (delete_intent != null) {
            builder.setDeleteIntent(
                    PendingIntent.getBroadcast(mContext, 0, delete_intent, PendingIntent.FLAG_UPDATE_CURRENT));
        }
        if (content_intent != null) {
            builder.setContentIntent(
                    PendingIntent.getActivity(mContext, 0, content_intent, PendingIntent.FLAG_UPDATE_CURRENT));
        }
        int defaults = 0;
        if (mPreferences.getBoolean(PREFERENCE_KEY_NOTIFICATION_HAVE_SOUND, false)) {
            builder.setSound(Uri.parse(mPreferences.getString(PREFERENCE_KEY_NOTIFICATION_RINGTONE,
                    Settings.System.DEFAULT_RINGTONE_URI.getPath())), Notification.STREAM_DEFAULT);
        }
        if (mPreferences.getBoolean(PREFERENCE_KEY_NOTIFICATION_HAVE_VIBRATION, false)) {
            defaults |= Notification.DEFAULT_VIBRATE;
        }
        if (mPreferences.getBoolean(PREFERENCE_KEY_NOTIFICATION_HAVE_LIGHTS, false)) {
            final int color_def = mResources.getColor(R.color.holo_blue_dark);
            final int color = mPreferences.getInt(PREFERENCE_KEY_NOTIFICATION_LIGHT_COLOR, color_def);
            builder.setLights(color, 1000, 2000);
        }
        builder.setDefaults(defaults);
        return builder.build();
    }

    private void showErrorToast(final int action_res, final Exception e, final boolean long_message) {
        Utils.showErrorToast(mContext, mContext.getString(action_res), e, long_message);
    }

    public static AsyncTwitterWrapper getInstance(final Context context) {
        if (sInstance != null)
            return sInstance;
        return sInstance = new AsyncTwitterWrapper(context);
    }

    public static class UpdateProfileBannerImageTask extends ManagedAsyncTask<Void, Void, SingleResponse<Integer>> {

        private final long account_id;
        private final Uri image_uri;
        private final boolean delete_image;
        private final Context mContext;

        public UpdateProfileBannerImageTask(final Context context, final AsyncTaskManager manager,
                final long account_id, final Uri image_uri, final boolean delete_image) {
            super(context, manager);
            mContext = context;
            this.account_id = account_id;
            this.image_uri = image_uri;
            this.delete_image = delete_image;
        }

        @Override
        protected SingleResponse<Integer> doInBackground(final Void... params) {
            return TwitterWrapper.updateProfileBannerImage(mContext, account_id, image_uri, delete_image);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Integer> result) {
            if (result != null && result.data != null) {
                if (result.data >= 200 && result.data <= 202) {
                    Toast.makeText(mContext, R.string.profile_banner_image_update_successfully, Toast.LENGTH_SHORT)
                            .show();
                } else {
                    Utils.showErrorToast(mContext, mContext.getString(R.string.updating_profile_banner_image),
                            "Code " + result.data, true);
                }
            } else {
                Utils.showErrorToast(mContext, R.string.updating_profile_banner_image, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_PROFILE_BANNER_UPDATED);
            intent.putExtra(INTENT_KEY_USER_ID, account_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    public static class UpdateProfileImageTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final Uri image_uri;
        private final boolean delete_image;
        private final Context context;

        public UpdateProfileImageTask(final Context context, final AsyncTaskManager manager, final long account_id,
                final Uri image_uri, final boolean delete_image) {
            super(context, manager);
            this.context = context;
            this.account_id = account_id;
            this.image_uri = image_uri;
            this.delete_image = delete_image;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {
            return TwitterWrapper.updateProfileImage(context, account_id, image_uri, delete_image);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null) {
                Toast.makeText(context, R.string.profile_image_update_successfully, Toast.LENGTH_SHORT).show();
            } else {
                Utils.showErrorToast(context, R.string.updating_profile_image, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_PROFILE_UPDATED);
            intent.putExtra(INTENT_KEY_USER_ID, account_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            context.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    public static class UpdateProfileTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final String name, url, location, description;
        private final Context context;

        public UpdateProfileTask(final Context context, final AsyncTaskManager manager, final long account_id,
                final String name, final String url, final String location, final String description) {
            super(context, manager);
            this.context = context;
            this.account_id = account_id;
            this.name = name;
            this.url = url;
            this.location = location;
            this.description = description;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {
            return updateProfile(context, account_id, name, url, location, description);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null) {
                Toast.makeText(context, R.string.profile_update_successfully, Toast.LENGTH_SHORT).show();
            } else {
                Utils.showErrorToast(context, context.getString(R.string.updating_profile), result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_PROFILE_IMAGE_UPDATED);
            intent.putExtra(INTENT_KEY_USER_ID, account_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            context.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class AddUserListMemberTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id, user_id;
        private final int list_id;
        private final String screen_name;

        public AddUserListMemberTask(final long account_id, final int list_id, final long user_id,
                final String screen_name) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_id = list_id;
            this.user_id = user_id;
            this.screen_name = screen_name;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    if (user_id > 0) {
                        final UserList list = twitter.addUserListMember(list_id, user_id);
                        return new TwitterSingleResponse<UserList>(account_id, list, null);
                    } else if (screen_name != null) {
                        final User user = twitter.showUser(screen_name);
                        if (user != null && user.getId() > 0) {
                            final UserList list = twitter.addUserListMember(list_id, user.getId());
                            return new TwitterSingleResponse<UserList>(account_id, list, null);
                        }
                    }
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.add_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.adding_member, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_MEMBER_DELETED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class CreateBlockTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id, user_id;

        public CreateBlockTask(final long account_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final User user = twitter.createBlock(user_id);
                    return new TwitterSingleResponse<User>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<User>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<User>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null && result.data.getId() > 0) {
                for (final Uri uri : STATUSES_URIS) {
                    final String where = Statuses.ACCOUNT_ID + " = " + account_id + " AND " + Statuses.USER_ID
                            + " = " + user_id;
                    mResolver.delete(uri, where, null);

                }
                // I bet you don't want to see this user in your auto complete
                // list.
                final String where = CachedUsers.USER_ID + " = " + user_id;
                mResolver.delete(CachedUsers.CONTENT_URI, where, null);
                Toast.makeText(mContext, R.string.user_blocked, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.blocking, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_BLOCKSTATE_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class CreateFavoriteTask extends ManagedAsyncTask<Void, Void, SingleResponse<twitter4j.Status>> {

        private final long account_id, status_id;

        public CreateFavoriteTask(final long account_id, final long status_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.status_id = status_id;
        }

        @Override
        protected SingleResponse<twitter4j.Status> doInBackground(final Void... params) {

            if (account_id < 0)
                return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final twitter4j.Status status = twitter.createFavorite(status_id);
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, status, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<twitter4j.Status> result) {

            if (result.data != null) {
                final long status_id = result.data.getId();
                final ContentValues values = new ContentValues();
                values.put(Statuses.IS_FAVORITE, 1);
                final StringBuilder where = new StringBuilder();
                where.append(Statuses.ACCOUNT_ID + " = " + result.extras.getLong(INTENT_KEY_ACCOUNT_ID));
                where.append(" AND ");
                where.append("(");
                where.append(Statuses.STATUS_ID + " = " + status_id);
                where.append(" OR ");
                where.append(Statuses.RETWEET_ID + " = " + status_id);
                where.append(")");
                for (final Uri uri : TweetStore.STATUSES_URIS) {
                    mResolver.update(uri, values, where.toString(), null);
                }
                final Intent intent = new Intent(BROADCAST_FAVORITE_CHANGED);
                intent.putExtra(INTENT_KEY_STATUS_ID, status_id);
                intent.putExtra(INTENT_KEY_FAVORITED, true);
                mContext.sendBroadcast(intent);
                Toast.makeText(mContext, R.string.favorite_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.favoriting, result.exception, true);
            }
            super.onPostExecute(result);
        }

    }

    class CreateFriendshipTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final long user_id;

        public CreateFriendshipTask(final long account_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final User user = twitter.createFriendship(user_id);
                    return new TwitterSingleResponse<User>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<User>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<User>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null) {
                Toast.makeText(mContext, R.string.follow_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.following, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_FRIENDSHIP_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class CreateMultiBlockTask extends ManagedAsyncTask<Void, Void, ListResponse<Long>> {

        private final long account_id;
        private final long[] user_ids;

        public CreateMultiBlockTask(final long account_id, final long[] user_ids) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_ids = user_ids;
        }

        @Override
        protected ListResponse<Long> doInBackground(final Void... params) {

            final Bundle bundle = new Bundle();
            bundle.putLong(INTENT_KEY_ACCOUNT_ID, account_id);
            final List<Long> blocked_users = new ArrayList<Long>();
            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                for (final long user_id : user_ids) {
                    try {
                        final User user = twitter.createBlock(user_id);
                        if (user == null || user.getId() <= 0) {
                            continue;
                        }
                        blocked_users.add(user.getId());
                    } catch (final TwitterException e) {
                        return new ListResponse<Long>(null, e, bundle);
                    }
                }
            }
            return new ListResponse<Long>(blocked_users, null, bundle);
        }

        @Override
        protected void onPostExecute(final ListResponse<Long> result) {
            if (result.list != null) {
                final String user_ids = ListUtils.toString(result.list, ',', false);
                for (final Uri uri : STATUSES_URIS) {
                    final String where = Statuses.ACCOUNT_ID + " = " + account_id + " AND " + Statuses.USER_ID
                            + " IN (" + user_ids + ")";
                    mResolver.delete(uri, where, null);
                }
                // I bet you don't want to see these users in your auto complete
                // list.
                final String where = CachedUsers.USER_ID + " IN (" + user_ids + ")";
                mResolver.delete(CachedUsers.CONTENT_URI, where, null);
                Toast.makeText(mContext, R.string.users_blocked, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.blocking, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_MULTI_BLOCKSTATE_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_ids);
            intent.putExtra(INTENT_KEY_SUCCEED, result.list != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class CreateUserListSubscriptionTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id;
        private final int list_id;

        public CreateUserListSubscriptionTask(final long account_id, final int list_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_id = list_id;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final UserList list = twitter.createUserListSubscription(list_id);
                    return new TwitterSingleResponse<UserList>(account_id, list, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.follow_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.following, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_SUBSCRIPTION_CHANGED);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class CreateUserListTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id;
        private final String list_name, description;
        private final boolean is_public;

        public CreateUserListTask(final long account_id, final String list_name, final boolean is_public,
                final String description) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_name = list_name;
            this.description = description;
            this.is_public = is_public;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    if (list_name != null) {
                        final UserList list = twitter.createUserList(list_name, is_public, description);
                        return new TwitterSingleResponse<UserList>(account_id, list, null);
                    }
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.create_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.creating_list, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_CREATED);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DeleteUserListMemberTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id, user_id;
        private final int list_id;

        public DeleteUserListMemberTask(final long account_id, final int list_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_id = list_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final UserList list = twitter.deleteUserListMember(list_id, user_id);
                    return new TwitterSingleResponse<UserList>(account_id, list, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.delete_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.deleting, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_MEMBER_DELETED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DestroyBlockTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final long user_id;

        public DestroyBlockTask(final long account_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final User user = twitter.destroyBlock(user_id);
                    return new TwitterSingleResponse<User>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<User>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<User>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null) {
                Toast.makeText(mContext, R.string.user_unblocked, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.unblocking, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_BLOCKSTATE_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DestroyDirectMessageTask extends ManagedAsyncTask<Void, Void, SingleResponse<DirectMessage>> {

        private final Twitter twitter;
        private final long message_id;
        private final long account_id;

        public DestroyDirectMessageTask(final long account_id, final long message_id) {
            super(mContext, mAsyncTaskManager);
            twitter = getTwitterInstance(mContext, account_id, false);
            this.account_id = account_id;
            this.message_id = message_id;
        }

        @Override
        protected SingleResponse<DirectMessage> doInBackground(final Void... args) {
            if (twitter == null)
                return new TwitterSingleResponse<DirectMessage>(account_id, null, null);
            try {
                return new TwitterSingleResponse<DirectMessage>(account_id,
                        twitter.destroyDirectMessage(message_id), null);
            } catch (final TwitterException e) {
                return new TwitterSingleResponse<DirectMessage>(account_id, null, e);
            }
        }

        @Override
        protected void onPostExecute(final SingleResponse<DirectMessage> result) {
            super.onPostExecute(result);
            if (result == null)
                return;
            if (result.data != null && result.data.getId() > 0) {
                Toast.makeText(mContext, R.string.delete_successfully, Toast.LENGTH_SHORT).show();
                final String where = DirectMessages.MESSAGE_ID + " = " + message_id;
                mResolver.delete(DirectMessages.Inbox.CONTENT_URI, where, null);
                mResolver.delete(DirectMessages.Outbox.CONTENT_URI, where, null);
            } else {
                showErrorToast(R.string.deleting, result.exception, true);
            }
        }

    }

    class DestroyFavoriteTask extends ManagedAsyncTask<Void, Void, SingleResponse<twitter4j.Status>> {

        private final long account_id;

        private final long status_id;

        public DestroyFavoriteTask(final long account_id, final long status_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.status_id = status_id;
        }

        @Override
        protected SingleResponse<twitter4j.Status> doInBackground(final Void... params) {

            if (account_id < 0) {
                new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);
            }

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final twitter4j.Status status = twitter.destroyFavorite(status_id);
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, status, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<twitter4j.Status> result) {

            if (result.data != null) {
                final long status_id = result.data.getId();
                final ContentValues values = new ContentValues();
                values.put(Statuses.IS_FAVORITE, 0);
                final StringBuilder where = new StringBuilder();
                where.append(Statuses.ACCOUNT_ID + " = " + result.extras.getLong(INTENT_KEY_ACCOUNT_ID));
                where.append(" AND ");
                where.append("(");
                where.append(Statuses.STATUS_ID + " = " + status_id);
                where.append(" OR ");
                where.append(Statuses.RETWEET_ID + " = " + status_id);
                where.append(")");
                for (final Uri uri : TweetStore.STATUSES_URIS) {
                    mResolver.update(uri, values, where.toString(), null);
                }
                final Intent intent = new Intent(BROADCAST_FAVORITE_CHANGED);
                intent.putExtra(INTENT_KEY_USER_ID, account_id);
                intent.putExtra(INTENT_KEY_STATUS_ID, status_id);
                intent.putExtra(INTENT_KEY_FAVORITED, false);
                mContext.sendBroadcast(intent);
                Toast.makeText(mContext, R.string.unfavorite_successfully, Toast.LENGTH_SHORT).show();

            } else {
                showErrorToast(R.string.unfavoriting, result.exception, true);
            }
            super.onPostExecute(result);
        }

    }

    class DestroyFriendshipTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final long user_id;

        public DestroyFriendshipTask(final long account_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final User user = twitter.destroyFriendship(user_id);
                    return new TwitterSingleResponse<User>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<User>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<User>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null) {
                Toast.makeText(mContext, R.string.unfollow_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.unfollowing, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_FRIENDSHIP_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DestroyStatusTask extends ManagedAsyncTask<Void, Void, SingleResponse<twitter4j.Status>> {

        private final long account_id;

        private final long status_id;

        public DestroyStatusTask(final long account_id, final long status_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.status_id = status_id;
        }

        @Override
        protected SingleResponse<twitter4j.Status> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final twitter4j.Status status = twitter.destroyStatus(status_id);
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, status, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<twitter4j.Status> result) {
            final Intent intent = new Intent(BROADCAST_STATUS_DESTROYED);
            if (result != null && result.data != null && result.data.getId() > 0) {
                final long status_id = result.data.getId();
                final ContentValues values = new ContentValues();
                values.put(Statuses.MY_RETWEET_ID, -1);
                for (final Uri uri : TweetStore.STATUSES_URIS) {
                    mResolver.delete(uri, Statuses.STATUS_ID + " = " + status_id, null);
                    mResolver.update(uri, values, Statuses.MY_RETWEET_ID + " = " + status_id, null);
                }
                intent.putExtra(INTENT_KEY_STATUS_ID, status_id);
                intent.putExtra(INTENT_KEY_SUCCEED, true);
                Toast.makeText(mContext, R.string.delete_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.deleting, result.exception, true);
            }
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DestroyUserListSubscriptionTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id;
        private final int list_id;

        public DestroyUserListSubscriptionTask(final long account_id, final int list_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_id = list_id;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final UserList list = twitter.destroyUserListSubscription(list_id);
                    return new TwitterSingleResponse<UserList>(account_id, list, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.unfollow_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.unfollowing, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_SUBSCRIPTION_CHANGED);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class DestroyUserListTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id;
        private final int list_id;

        public DestroyUserListTask(final long account_id, final int list_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.list_id = list_id;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    if (list_id > 0) {
                        final UserList list = twitter.destroyUserList(list_id);
                        return new TwitterSingleResponse<UserList>(account_id, list, null);
                    }
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final boolean succeed = result != null && result.data != null && result.data.getId() > 0;
            if (succeed) {
                Toast.makeText(mContext, R.string.delete_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.deleting, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_USER_LIST_DELETED);
            intent.putExtra(INTENT_KEY_SUCCEED, succeed);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    abstract class GetDirectMessagesTask
            extends ManagedAsyncTask<Void, Void, List<TwitterListResponse<DirectMessage>>> {

        private final long[] account_ids, max_ids, since_ids;

        public GetDirectMessagesTask(final long[] account_ids, final long[] max_ids, final long[] since_ids,
                final String tag) {
            super(mContext, mAsyncTaskManager, tag);
            this.account_ids = account_ids;
            this.max_ids = max_ids;
            this.since_ids = since_ids;
        }

        public abstract ResponseList<DirectMessage> getDirectMessages(Twitter twitter, Paging paging)
                throws TwitterException;

        @Override
        protected List<TwitterListResponse<DirectMessage>> doInBackground(final Void... params) {

            final List<TwitterListResponse<DirectMessage>> result = new ArrayList<TwitterListResponse<DirectMessage>>();

            if (account_ids == null)
                return result;

            int idx = 0;
            final int load_item_limit = mPreferences.getInt(PREFERENCE_KEY_LOAD_ITEM_LIMIT,
                    PREFERENCE_DEFAULT_LOAD_ITEM_LIMIT);
            for (final long account_id : account_ids) {
                final Twitter twitter = getTwitterInstance(mContext, account_id, true);
                if (twitter != null) {
                    try {
                        final Paging paging = new Paging();
                        paging.setCount(load_item_limit);
                        long max_id = -1, since_id = -1;
                        if (isMaxIdsValid() && max_ids[idx] > 0) {
                            max_id = max_ids[idx];
                            paging.setMaxId(max_id);
                        }
                        if (isSinceIdsValid() && since_ids[idx] > 0) {
                            since_id = since_ids[idx];
                            paging.setSinceId(since_id);
                        }
                        final ResponseList<DirectMessage> statuses = getDirectMessages(twitter, paging);

                        if (statuses != null) {
                            result.add(new TwitterListResponse<DirectMessage>(account_id, max_id, since_id,
                                    load_item_limit, statuses, null));
                        }
                    } catch (final TwitterException e) {
                        result.add(new TwitterListResponse<DirectMessage>(account_id, -1, -1, load_item_limit, null,
                                e));
                    }
                }
                idx++;
            }
            return result;

        }

        @Override
        protected void onPostExecute(final List<TwitterListResponse<DirectMessage>> result) {
            super.onPostExecute(result);
            for (final TwitterListResponse<DirectMessage> response : result) {
                if (response.list == null) {
                    showErrorToast(R.string.refreshing_direct_messages, response.exception, true);
                }
            }
        }

        final boolean isMaxIdsValid() {
            return max_ids != null && max_ids.length == account_ids.length;
        }

        final boolean isSinceIdsValid() {
            return since_ids != null && since_ids.length == account_ids.length;
        }

    }

    class GetHomeTimelineTask extends GetStatusesTask {

        public GetHomeTimelineTask(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
            super(account_ids, max_ids, since_ids, TASK_TAG_GET_HOME_TIMELINE);
        }

        @Override
        public ResponseList<twitter4j.Status> getStatuses(final Twitter twitter, final Paging paging)
                throws TwitterException {
            return twitter.getHomeTimeline(paging);
        }

        @Override
        public Twitter getTwitter(final long account_id) {
            return getTwitterInstance(mContext, account_id, true);
        }

        @Override
        protected void onPostExecute(final List<StatusListResponse> responses) {
            super.onPostExecute(responses);
            mAsyncTaskManager.add(new StoreHomeTimelineTask(responses, shouldSetMinId(), !isMaxIdsValid()), true);
            mGetHomeTimelineTaskId = -1;
        }

        @Override
        protected void onPreExecute() {
            final Intent intent = new Intent(BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING);
            mContext.sendBroadcast(intent);
            super.onPreExecute();
        }

    }

    class GetLocalTrendsTask extends GetTrendsTask {

        private final int woeid;

        public GetLocalTrendsTask(final long account_id, final int woeid) {
            super(account_id);
            this.woeid = woeid;
        }

        @Override
        public List<Trends> getTrends(final Twitter twitter) throws TwitterException {
            final ArrayList<Trends> trends_list = new ArrayList<Trends>();
            if (twitter != null) {
                trends_list.add(twitter.getLocationTrends(woeid));
            }
            return trends_list;
        }

        @Override
        protected void onPostExecute(final ListResponse<Trends> result) {
            mAsyncTaskManager.add(new StoreLocalTrendsTask(result), true);
            super.onPostExecute(result);

        }

    }

    class GetMentionsTask extends GetStatusesTask {

        public GetMentionsTask(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
            super(account_ids, max_ids, since_ids, TASK_TAG_GET_MENTIONS);
        }

        @Override
        public ResponseList<twitter4j.Status> getStatuses(final Twitter twitter, final Paging paging)
                throws TwitterException {
            return twitter.getMentions(paging);
        }

        @Override
        public Twitter getTwitter(final long account_id) {
            return getTwitterInstance(mContext, account_id, true);
        }

        @Override
        protected void onPostExecute(final List<StatusListResponse> responses) {
            super.onPostExecute(responses);
            mAsyncTaskManager.add(new StoreMentionsTask(responses, shouldSetMinId(), !isMaxIdsValid()), true);
            mGetMentionsTaskId = -1;
        }

        @Override
        protected void onPreExecute() {

            final Intent intent = new Intent(BROADCAST_RESCHEDULE_MENTIONS_REFRESHING);
            mContext.sendBroadcast(intent);
            super.onPreExecute();
        }

    }

    class GetReceivedDirectMessagesTask extends GetDirectMessagesTask {

        public GetReceivedDirectMessagesTask(final long[] account_ids, final long[] max_ids,
                final long[] since_ids) {
            super(account_ids, max_ids, since_ids, TASK_TAG_GET_RECEIVED_DIRECT_MESSAGES);
        }

        @Override
        public ResponseList<DirectMessage> getDirectMessages(final Twitter twitter, final Paging paging)
                throws TwitterException {
            return twitter.getDirectMessages(paging);
        }

        @Override
        protected void onPostExecute(final List<TwitterListResponse<DirectMessage>> responses) {
            super.onPostExecute(responses);
            mAsyncTaskManager.add(new StoreReceivedDirectMessagesTask(responses, !isMaxIdsValid()), true);
            mGetReceivedDirectMessagesTaskId = -1;
        }

        @Override
        protected void onPreExecute() {
            final Intent intent = new Intent(BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING);
            mContext.sendBroadcast(intent);
            super.onPreExecute();
        }

    }

    class GetSentDirectMessagesTask extends GetDirectMessagesTask {

        public GetSentDirectMessagesTask(final long[] account_ids, final long[] max_ids, final long[] since_ids) {
            super(account_ids, max_ids, since_ids, TASK_TAG_GET_SENT_DIRECT_MESSAGES);
        }

        @Override
        public ResponseList<DirectMessage> getDirectMessages(final Twitter twitter, final Paging paging)
                throws TwitterException {
            return twitter.getSentDirectMessages(paging);
        }

        @Override
        protected void onPostExecute(final List<TwitterListResponse<DirectMessage>> responses) {
            super.onPostExecute(responses);
            mAsyncTaskManager.add(new StoreSentDirectMessagesTask(responses, !isMaxIdsValid()), true);
            mGetSentDirectMessagesTaskId = -1;
        }

    }

    abstract class GetStatusesTask extends ManagedAsyncTask<Void, Void, List<StatusListResponse>> {

        private final long[] account_ids, max_ids, since_ids;

        public GetStatusesTask(final long[] account_ids, final long[] max_ids, final long[] since_ids,
                final String tag) {
            super(mContext, mAsyncTaskManager, tag);
            this.account_ids = account_ids;
            this.max_ids = max_ids;
            this.since_ids = since_ids;
        }

        public abstract ResponseList<twitter4j.Status> getStatuses(Twitter twitter, Paging paging)
                throws TwitterException;

        public abstract Twitter getTwitter(long account_id);

        @Override
        protected List<StatusListResponse> doInBackground(final Void... params) {

            final List<StatusListResponse> result = new ArrayList<StatusListResponse>();

            if (account_ids == null)
                return result;

            int idx = 0;
            final int load_item_limit = mPreferences.getInt(PREFERENCE_KEY_LOAD_ITEM_LIMIT,
                    PREFERENCE_DEFAULT_LOAD_ITEM_LIMIT);
            for (final long account_id : account_ids) {
                final Twitter twitter = getTwitter(account_id);
                if (twitter != null) {
                    try {
                        final Paging paging = new Paging();
                        paging.setCount(load_item_limit);
                        long max_id = -1, since_id = -1;
                        if (isMaxIdsValid() && max_ids[idx] > 0) {
                            max_id = max_ids[idx];
                            paging.setMaxId(max_id);
                        }
                        if (isSinceIdsValid() && since_ids[idx] > 0) {
                            since_id = since_ids[idx];
                            paging.setSinceId(since_id);
                        }
                        final ResponseList<twitter4j.Status> statuses = getStatuses(twitter, paging);
                        if (statuses != null) {
                            result.add(new StatusListResponse(account_id, max_id, since_id, load_item_limit,
                                    statuses, null));
                        }
                    } catch (final TwitterException e) {
                        result.add(new StatusListResponse(account_id, -1, -1, load_item_limit, null, e));
                    }
                }
                idx++;
            }
            return result;
        }

        @Override
        protected void onPostExecute(final List<StatusListResponse> result) {
            super.onPostExecute(result);
            for (final StatusListResponse response : result) {
                if (response.list == null) {
                    showErrorToast(R.string.refreshing_timelines, response.exception, true);
                }
            }
        }

        final boolean isMaxIdsValid() {
            return max_ids != null && max_ids.length == account_ids.length;
        }

        final boolean isSinceIdsValid() {
            return since_ids != null && since_ids.length == account_ids.length;
        }

        final boolean shouldSetMinId() {
            return !isMaxIdsValid();
        }

    }

    abstract class GetTrendsTask extends ManagedAsyncTask<Void, Void, ListResponse<Trends>> {

        private final long account_id;

        public GetTrendsTask(final long account_id) {
            super(mContext, mAsyncTaskManager, TASK_TAG_GET_TRENDS);
            this.account_id = account_id;
        }

        public abstract List<Trends> getTrends(Twitter twitter) throws TwitterException;

        @Override
        protected ListResponse<Trends> doInBackground(final Void... params) {
            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            final Bundle extras = new Bundle();
            extras.putLong(INTENT_KEY_ACCOUNT_ID, account_id);
            if (twitter != null) {
                try {
                    return new ListResponse<Trends>(getTrends(twitter), null, extras);
                } catch (final TwitterException e) {
                    return new ListResponse<Trends>(null, e, extras);
                }
            }
            return new ListResponse<Trends>(null, null, extras);
        }

    }

    class ReportMultiSpamTask extends ManagedAsyncTask<Void, Void, ListResponse<Long>> {

        private final long account_id;
        private final long[] user_ids;

        public ReportMultiSpamTask(final long account_id, final long[] user_ids) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_ids = user_ids;
        }

        @Override
        protected ListResponse<Long> doInBackground(final Void... params) {

            final Bundle extras = new Bundle();
            extras.putLong(INTENT_KEY_ACCOUNT_ID, account_id);
            final List<Long> reported_users = new ArrayList<Long>();
            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                for (final long user_id : user_ids) {
                    try {
                        final User user = twitter.reportSpam(user_id);
                        if (user == null || user.getId() <= 0) {
                            continue;
                        }
                        reported_users.add(user.getId());
                    } catch (final TwitterException e) {
                        return new ListResponse<Long>(null, e, extras);
                    }
                }
            }
            return new ListResponse<Long>(reported_users, null, extras);
        }

        @Override
        protected void onPostExecute(final ListResponse<Long> result) {
            if (result != null) {
                final String user_id_where = ListUtils.toString(result.list, ',', false);
                for (final Uri uri : STATUSES_URIS) {
                    final String where = Statuses.ACCOUNT_ID + " = " + account_id + " AND " + Statuses.USER_ID
                            + " IN (" + user_id_where + ")";
                    mResolver.delete(uri, where, null);
                }
                Toast.makeText(mContext, R.string.reported_users_for_spam, Toast.LENGTH_SHORT).show();
            }
            final Intent intent = new Intent(BROADCAST_MULTI_BLOCKSTATE_CHANGED);
            intent.putExtra(INTENT_KEY_USER_IDS, user_ids);
            intent.putExtra(INTENT_KEY_ACCOUNT_ID, account_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class ReportSpamTask extends ManagedAsyncTask<Void, Void, SingleResponse<User>> {

        private final long account_id;
        private final long user_id;

        public ReportSpamTask(final long account_id, final long user_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.user_id = user_id;
        }

        @Override
        protected SingleResponse<User> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final User user = twitter.reportSpam(user_id);
                    return new TwitterSingleResponse<User>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<User>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<User>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<User> result) {
            if (result != null && result.data != null && result.data.getId() > 0) {
                for (final Uri uri : STATUSES_URIS) {
                    final String where = Statuses.ACCOUNT_ID + " = " + account_id + " AND " + Statuses.USER_ID
                            + " = " + user_id;
                    mResolver.delete(uri, where, null);
                }
                Toast.makeText(mContext, R.string.reported_user_for_spam, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.reporting_for_spam, result.exception, true);
            }
            final Intent intent = new Intent(BROADCAST_BLOCKSTATE_CHANGED);
            intent.putExtra(INTENT_KEY_USER_ID, user_id);
            intent.putExtra(INTENT_KEY_SUCCEED, result != null && result.data != null);
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

    class RetweetStatusTask extends ManagedAsyncTask<Void, Void, SingleResponse<twitter4j.Status>> {

        private final long account_id;

        private final long status_id;

        public RetweetStatusTask(final long account_id, final long status_id) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.status_id = status_id;
        }

        @Override
        protected SingleResponse<twitter4j.Status> doInBackground(final Void... params) {

            if (account_id < 0)
                return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final twitter4j.Status status = twitter.retweetStatus(status_id);
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, status, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<twitter4j.Status>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<twitter4j.Status>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<twitter4j.Status> result) {

            if (result.data != null && result.data.getId() > 0) {
                final ContentValues values = new ContentValues();
                values.put(Statuses.MY_RETWEET_ID, result.data.getId());
                final String where = Statuses.STATUS_ID + " = " + status_id + " OR " + Statuses.RETWEET_ID + " = "
                        + status_id;
                for (final Uri uri : STATUSES_URIS) {
                    mResolver.update(uri, values, where, null);
                }
                final Intent intent = new Intent(BROADCAST_RETWEET_CHANGED);
                intent.putExtra(INTENT_KEY_STATUS_ID, status_id);
                intent.putExtra(INTENT_KEY_RETWEETED, true);
                mContext.sendBroadcast(intent);
                Toast.makeText(mContext, R.string.retweet_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.retweeting, result.exception, true);
            }

            super.onPostExecute(result);
        }

    }

    class SendDirectMessageTask extends ManagedAsyncTask<Void, Void, SingleResponse<DirectMessage>> {

        private final Twitter twitter;
        private final long user_id;
        private final String screen_name;
        private final String message;
        private final long account_id;

        public SendDirectMessageTask(final long account_id, final String screen_name, final long user_id,
                final String message) {
            super(mContext, mAsyncTaskManager);
            // A very stupid workaround here, in order to send tweets
            // contains asterisk symbol.
            twitter = getTwitterInstance(mContext, account_id, false, !message.contains("*"));
            this.account_id = account_id;
            this.user_id = user_id;
            this.screen_name = screen_name;
            this.message = message;
        }

        @Override
        protected SingleResponse<DirectMessage> doInBackground(final Void... args) {
            if (twitter == null)
                return new TwitterSingleResponse<DirectMessage>(account_id, null, null);
            try {
                if (user_id > 0)
                    return new TwitterSingleResponse<DirectMessage>(account_id,
                            twitter.sendDirectMessage(user_id, message), null);
                else if (screen_name != null)
                    return new TwitterSingleResponse<DirectMessage>(account_id,
                            twitter.sendDirectMessage(screen_name, message), null);
            } catch (final TwitterException e) {
                return new TwitterSingleResponse<DirectMessage>(account_id, null, e);
            }
            return new TwitterSingleResponse<DirectMessage>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<DirectMessage> result) {
            super.onPostExecute(result);
            if (result == null)
                return;
            if (result.data != null && result.data.getId() > 0) {
                final ContentValues values = makeDirectMessageContentValues(result.data, account_id, true,
                        large_profile_image);
                mResolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
                Toast.makeText(mContext, R.string.send_successfully, Toast.LENGTH_SHORT).show();
            } else {
                showErrorToast(R.string.sending_direct_message, result.exception, true);
            }
        }

    }

    abstract class StoreDirectMessagesTask extends ManagedAsyncTask<Void, Void, SingleResponse<Bundle>> {

        private final List<TwitterListResponse<DirectMessage>> responses;
        private final Uri uri;

        public StoreDirectMessagesTask(final List<TwitterListResponse<DirectMessage>> result, final Uri uri,
                final boolean notify, final String tag) {
            super(mContext, mAsyncTaskManager, tag);
            responses = result;
            this.uri = uri.buildUpon().appendQueryParameter(QUERY_PARAM_NOTIFY, String.valueOf(notify)).build();
        }

        @Override
        protected SingleResponse<Bundle> doInBackground(final Void... args) {

            boolean succeed = false;
            for (final TwitterListResponse<DirectMessage> response : responses) {
                final long account_id = response.account_id;
                final List<DirectMessage> messages = response.list;
                if (messages != null) {
                    final List<ContentValues> values_list = new ArrayList<ContentValues>();
                    final List<Long> message_ids = new ArrayList<Long>();

                    for (final DirectMessage message : messages) {
                        if (message == null || message.getId() <= 0) {
                            continue;
                        }
                        message_ids.add(message.getId());
                        values_list.add(makeDirectMessageContentValues(message, account_id, isOutgoing(),
                                large_profile_image));
                    }

                    // Delete all rows conflicting before new data inserted.
                    {
                        final StringBuilder where = new StringBuilder();
                        where.append(DirectMessages.ACCOUNT_ID + " = " + account_id);
                        where.append(" AND ");
                        where.append(DirectMessages.MESSAGE_ID + " IN ( "
                                + ListUtils.toString(message_ids, ',', true) + " ) ");
                        final Uri delete_uri = appendQueryParameters(uri,
                                new NameValuePairImpl(QUERY_PARAM_NOTIFY, false));
                        mResolver.delete(delete_uri, where.toString(), null);
                    }

                    // Insert previously fetched items.
                    final Uri insert_uri = appendQueryParameters(uri,
                            new NameValuePairImpl(QUERY_PARAM_NOTIFY, false));
                    mResolver.bulkInsert(insert_uri, values_list.toArray(new ContentValues[values_list.size()]));

                }
                succeed = true;
            }
            final Bundle bundle = new Bundle();
            bundle.putBoolean(INTENT_KEY_SUCCEED, succeed);
            return new TwitterSingleResponse<Bundle>(-1, bundle, null);
        }

        abstract boolean isOutgoing();

    }

    class StoreHomeTimelineTask extends StoreStatusesTask {

        public StoreHomeTimelineTask(final List<StatusListResponse> result, final boolean should_set_min_id,
                final boolean notify) {
            super(result, Statuses.CONTENT_URI, should_set_min_id, notify, TASK_TAG_STORE_HOME_TIMELINE);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Bundle> response) {
            final boolean succeed = response != null && response.data != null
                    && response.data.getBoolean(INTENT_KEY_SUCCEED);
            final Bundle extras = new Bundle();
            extras.putBoolean(INTENT_KEY_SUCCEED, succeed);
            if (shouldSetMinId()) {
                final long min_id = response != null && response.data != null
                        ? response.data.getLong(INTENT_KEY_MIN_ID, -1)
                        : -1;
                extras.putLong(INTENT_KEY_MIN_ID, min_id);
                mPreferences.edit().putLong(PREFERENCE_KEY_SAVED_HOME_TIMELINE_ID, min_id).commit();
            }
            mContext.sendBroadcast(new Intent(BROADCAST_HOME_TIMELINE_REFRESHED).putExtras(extras));
            super.onPostExecute(response);
        }

    }

    class StoreLocalTrendsTask extends StoreTrendsTask {

        public StoreLocalTrendsTask(final ListResponse<Trends> result) {
            super(result, CachedTrends.Local.CONTENT_URI);
        }

    }

    class StoreMentionsTask extends StoreStatusesTask {

        public StoreMentionsTask(final List<StatusListResponse> result, final boolean should_set_min_id,
                final boolean notify) {
            super(result, Mentions.CONTENT_URI, should_set_min_id, notify, TASK_TAG_STORE_MENTIONS);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Bundle> response) {
            final boolean succeed = response != null && response.data != null
                    && response.data.getBoolean(INTENT_KEY_SUCCEED);
            final Bundle extras = new Bundle();
            extras.putBoolean(INTENT_KEY_SUCCEED, succeed);
            if (shouldSetMinId()) {
                final long min_id = response != null && response.data != null
                        ? response.data.getLong(INTENT_KEY_MIN_ID, -1)
                        : -1;
                extras.putLong(INTENT_KEY_MIN_ID, min_id);
                mPreferences.edit().putLong(PREFERENCE_KEY_SAVED_MENTIONS_LIST_ID, min_id).commit();
            }
            mContext.sendBroadcast(new Intent(BROADCAST_MENTIONS_REFRESHED).putExtras(extras));
            super.onPostExecute(response);
        }

    }

    class StoreReceivedDirectMessagesTask extends StoreDirectMessagesTask {

        public StoreReceivedDirectMessagesTask(final List<TwitterListResponse<DirectMessage>> result,
                final boolean notify) {
            super(result, DirectMessages.Inbox.CONTENT_URI, notify, TASK_TAG_STORE_RECEIVED_DIRECT_MESSAGES);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Bundle> response) {
            final boolean succeed = response != null && response.data != null
                    && response.data.getBoolean(INTENT_KEY_SUCCEED);
            mContext.sendBroadcast(
                    new Intent(BROADCAST_RECEIVED_DIRECT_MESSAGES_REFRESHED).putExtra(INTENT_KEY_SUCCEED, succeed));
            super.onPostExecute(response);
        }

        @Override
        boolean isOutgoing() {
            return false;
        }

    }

    class StoreSentDirectMessagesTask extends StoreDirectMessagesTask {

        public StoreSentDirectMessagesTask(final List<TwitterListResponse<DirectMessage>> result,
                final boolean notify) {
            super(result, DirectMessages.Outbox.CONTENT_URI, notify, TASK_TAG_STORE_SENT_DIRECT_MESSAGES);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Bundle> response) {
            final boolean succeed = response != null && response.data != null
                    && response.data.getBoolean(INTENT_KEY_SUCCEED);
            mContext.sendBroadcast(
                    new Intent(BROADCAST_SENT_DIRECT_MESSAGES_REFRESHED).putExtra(INTENT_KEY_SUCCEED, succeed));
            super.onPostExecute(response);
        }

        @Override
        boolean isOutgoing() {
            return true;
        }

    }

    abstract class StoreStatusesTask extends ManagedAsyncTask<Void, Void, SingleResponse<Bundle>> {

        private final List<StatusListResponse> responses;
        private final Uri uri;
        private final boolean should_set_min_id;
        private final ArrayList<ContentValues> all_statuses = new ArrayList<ContentValues>();

        public StoreStatusesTask(final List<StatusListResponse> result, final Uri uri,
                final boolean should_set_min_id, final boolean notify, final String tag) {
            super(mContext, mAsyncTaskManager, tag);
            responses = result;
            this.should_set_min_id = should_set_min_id;
            this.uri = uri.buildUpon().appendQueryParameter(QUERY_PARAM_NOTIFY, String.valueOf(notify)).build();
        }

        public boolean shouldSetMinId() {
            return should_set_min_id;
        }

        @Override
        protected SingleResponse<Bundle> doInBackground(final Void... args) {
            boolean succeed = false;

            final ArrayList<Long> newly_inserted_ids = new ArrayList<Long>();
            for (final StatusListResponse response : responses) {
                final long account_id = response.account_id;
                final List<twitter4j.Status> statuses = response.list;
                if (statuses == null || statuses.size() <= 0) {
                    continue;
                }
                final ArrayList<Long> ids_in_db = getStatusIdsInDatabase(mContext, uri, account_id);
                final boolean no_items_before = ids_in_db.size() <= 0;
                final List<ContentValues> values_list = new ArrayList<ContentValues>();
                final List<Long> status_ids = new ArrayList<Long>(), retweet_ids = new ArrayList<Long>();
                for (final twitter4j.Status status : statuses) {
                    if (status == null) {
                        continue;
                    }
                    final long status_id = status.getId();
                    final long retweet_id = status.getRetweetedStatus() != null
                            ? status.getRetweetedStatus().getId()
                            : -1;

                    status_ids.add(status_id);

                    if ((retweet_id <= 0 || !retweet_ids.contains(retweet_id))
                            && !retweet_ids.contains(status_id)) {
                        if (retweet_id > 0) {
                            retweet_ids.add(retweet_id);
                        }
                        values_list.add(makeStatusContentValues(status, account_id, large_profile_image));
                    }

                }

                // Delete all rows conflicting before new data inserted.

                final ArrayList<Long> account_newly_inserted = new ArrayList<Long>();
                account_newly_inserted.addAll(status_ids);
                account_newly_inserted.removeAll(ids_in_db);
                newly_inserted_ids.addAll(account_newly_inserted);
                final StringBuilder delete_where = new StringBuilder();
                final String ids_string = ListUtils.toString(status_ids, ',', true);
                delete_where.append(Statuses.ACCOUNT_ID + " = " + account_id);
                delete_where.append(" AND ");
                delete_where.append("(");
                delete_where.append(Statuses.STATUS_ID + " IN ( " + ids_string + " ) ");
                delete_where.append(" OR ");
                delete_where.append(Statuses.RETWEET_ID + " IN ( " + ids_string + " ) ");
                delete_where.append(")");
                final Uri delete_uri = appendQueryParameters(uri, new NameValuePairImpl(QUERY_PARAM_NOTIFY, false));
                final int rows_deleted = mResolver.delete(delete_uri, delete_where.toString(), null);
                // UCD
                final String UCD_new_status_ids = ListUtils.toString(account_newly_inserted, ',', true);
                ProfilingUtil.profile(mContext, account_id, "Download tweets, " + UCD_new_status_ids);
                all_statuses.addAll(values_list);
                // Insert previously fetched items.
                final Uri insert_query = appendQueryParameters(uri,
                        new NameValuePairImpl(QUERY_PARAM_NEW_ITEMS_COUNT,
                                newly_inserted_ids.size() - rows_deleted),
                        new NameValuePairImpl(QUERY_PARAM_NOTIFY, false));
                mResolver.bulkInsert(insert_query, values_list.toArray(new ContentValues[values_list.size()]));

                // Insert a gap.
                // TODO make sure it will not have bugs.
                final long min_id = status_ids.size() > 0 ? Collections.min(status_ids) : -1;
                final boolean insert_gap = min_id > 0 && response.load_item_limit <= response.list.size()
                        && !no_items_before;
                if (insert_gap) {
                    final ContentValues values = new ContentValues();
                    values.put(Statuses.IS_GAP, 1);
                    final StringBuilder where = new StringBuilder();
                    where.append(Statuses.ACCOUNT_ID + " = " + account_id);
                    where.append(" AND " + Statuses.STATUS_ID + " = " + min_id);
                    final Uri update_uri = appendQueryParameters(uri,
                            new NameValuePairImpl(QUERY_PARAM_NOTIFY, false));
                    mResolver.update(update_uri, values, where.toString(), null);
                    // Ignore gaps
                    newly_inserted_ids.remove(min_id);
                }
                succeed = true;
            }
            final Bundle bundle = new Bundle();
            bundle.putBoolean(INTENT_KEY_SUCCEED, succeed);
            getAllStatusesIds(mContext, uri);
            if (should_set_min_id && newly_inserted_ids.size() > 0) {
                bundle.putLong(INTENT_KEY_MIN_ID, Collections.min(newly_inserted_ids));
            }
            return new TwitterSingleResponse<Bundle>(-1, bundle, null);
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            final StatusListResponse[] array = new StatusListResponse[responses.size()];
            new CacheUsersStatusesTask(mContext, responses.toArray(array)).execute();
        }

    }

    class StoreTrendsTask extends ManagedAsyncTask<Void, Void, SingleResponse<Bundle>> {

        private final ListResponse<Trends> response;
        private final Uri uri;

        public StoreTrendsTask(final ListResponse<Trends> result, final Uri uri) {
            super(mContext, mAsyncTaskManager, TASK_TAG_STORE_TRENDS);
            response = result;
            this.uri = uri;
        }

        @Override
        protected SingleResponse<Bundle> doInBackground(final Void... args) {
            final Bundle bundle = new Bundle();
            if (response != null) {

                final List<Trends> messages = response.list;
                final ArrayList<String> hashtags = new ArrayList<String>();
                final ArrayList<ContentValues> hashtag_values = new ArrayList<ContentValues>();
                if (messages != null && messages.size() > 0) {
                    final ContentValues[] values_array = makeTrendsContentValues(messages);
                    for (final ContentValues values : values_array) {
                        final String hashtag = values.getAsString(CachedTrends.NAME).replaceFirst("#", "");
                        hashtags.add(hashtag);
                        final ContentValues hashtag_value = new ContentValues();
                        hashtag_value.put(CachedHashtags.NAME, hashtag);
                        hashtag_values.add(hashtag_value);
                    }
                    mResolver.delete(uri, null, null);
                    mResolver.bulkInsert(uri, values_array);
                    mResolver.delete(CachedHashtags.CONTENT_URI,
                            CachedHashtags.NAME + " IN (" + ListUtils.toStringForSQL(hashtags.size()) + ")",
                            hashtags.toArray(new String[hashtags.size()]));
                    mResolver.bulkInsert(CachedHashtags.CONTENT_URI,
                            hashtag_values.toArray(new ContentValues[hashtag_values.size()]));
                    bundle.putBoolean(INTENT_KEY_SUCCEED, true);
                }
            }
            return new TwitterSingleResponse<Bundle>(-1, bundle, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<Bundle> response) {
            if (response != null && response.data != null && response.data.getBoolean(INTENT_KEY_SUCCEED)) {
                final Intent intent = new Intent(BROADCAST_TRENDS_UPDATED);
                intent.putExtra(INTENT_KEY_SUCCEED, true);
                mContext.sendBroadcast(intent);
            }
            super.onPostExecute(response);

        }

    }

    class UpdateStatusTask extends ManagedAsyncTask<Void, Void, List<TwitterSingleResponse<twitter4j.Status>>> {

        private final ImageUploaderInterface uploader;
        private final TweetShortenerInterface shortener;

        private final Validator validator = new Validator();
        private final long[] account_ids;
        private final String content;

        private final ParcelableLocation location;
        private final Uri image_uri;
        private final long in_reply_to;
        private final boolean use_uploader, use_shortener, is_possibly_sensitive, delete_image;

        public UpdateStatusTask(final long[] account_ids, final String content, final ParcelableLocation location,
                final Uri image_uri, final long in_reply_to, final boolean is_possibly_sensitive,
                final boolean delete_image) {
            super(mContext, mAsyncTaskManager);
            final String uploader_component = mPreferences.getString(PREFERENCE_KEY_IMAGE_UPLOADER, null);
            final String shortener_component = mPreferences.getString(PREFERENCE_KEY_TWEET_SHORTENER, null);
            use_uploader = !isEmpty(uploader_component);
            final TwidereApplication app = TwidereApplication.getInstance(mContext);
            uploader = use_uploader ? ImageUploaderInterface.getInstance(app, uploader_component) : null;
            use_shortener = !isEmpty(shortener_component);
            shortener = use_shortener ? TweetShortenerInterface.getInstance(app, shortener_component) : null;
            this.account_ids = account_ids != null ? account_ids : new long[0];
            this.content = content;
            this.location = location;
            this.image_uri = image_uri;
            this.in_reply_to = in_reply_to;
            this.is_possibly_sensitive = is_possibly_sensitive;
            this.delete_image = delete_image;
        }

        @Override
        protected List<TwitterSingleResponse<twitter4j.Status>> doInBackground(final Void... params) {

            final Extractor extractor = new Extractor();
            final ArrayList<ContentValues> hashtag_values = new ArrayList<ContentValues>();
            final List<String> hashtags = extractor.extractHashtags(content);
            for (final String hashtag : hashtags) {
                final ContentValues values = new ContentValues();
                values.put(CachedHashtags.NAME, hashtag);
                hashtag_values.add(values);
            }
            mResolver.delete(CachedHashtags.CONTENT_URI,
                    CachedHashtags.NAME + " IN (" + ListUtils.toStringForSQL(hashtags.size()) + ")",
                    hashtags.toArray(new String[hashtags.size()]));
            mResolver.bulkInsert(CachedHashtags.CONTENT_URI,
                    hashtag_values.toArray(new ContentValues[hashtag_values.size()]));

            final List<TwitterSingleResponse<twitter4j.Status>> result = new ArrayList<TwitterSingleResponse<twitter4j.Status>>();

            if (account_ids.length == 0)
                return result;

            try {
                if (use_uploader && uploader == null)
                    throw new ImageUploaderNotFoundException();
                if (use_shortener && shortener == null)
                    throw new TweetShortenerNotFoundException();

                final String image_path = getImagePathFromUri(mContext, image_uri);
                final File image_file = image_path != null ? new File(image_path) : null;

                final Uri upload_result_uri;
                try {
                    if (uploader != null) {
                        uploader.waitForService();
                    }
                    upload_result_uri = image_file != null && image_file.exists() && uploader != null
                            ? uploader.upload(Uri.fromFile(image_file), content)
                            : null;
                } catch (final Exception e) {
                    throw new ImageUploadException();
                }
                if (use_uploader && image_file != null && image_file.exists() && upload_result_uri == null)
                    throw new ImageUploadException();

                final String unshortened_content = use_uploader && upload_result_uri != null
                        ? getImageUploadStatus(mContext, upload_result_uri.toString(), content)
                        : content;

                final boolean should_shorten = unshortened_content != null && unshortened_content.length() > 0
                        && !validator.isValidTweet(unshortened_content);
                final String screen_name = getAccountScreenName(mContext, account_ids[0]);
                final String shortened_content;
                try {
                    if (shortener != null) {
                        shortener.waitForService();
                    }
                    shortened_content = should_shorten && use_shortener
                            ? shortener.shorten(unshortened_content, screen_name, in_reply_to)
                            : null;
                } catch (final Exception e) {
                    throw new TweetShortenException();
                }

                if (should_shorten) {
                    if (!use_shortener)
                        throw new StatusTooLongException();
                    else if (unshortened_content == null)
                        throw new TweetShortenException();
                }

                final StatusUpdate status = new StatusUpdate(
                        should_shorten && use_shortener ? shortened_content : unshortened_content);
                status.setInReplyToStatusId(in_reply_to);
                if (location != null) {
                    status.setLocation(ParcelableLocation.toGeoLocation(location));
                }
                if (!use_uploader && image_file != null && image_file.exists()) {
                    status.setMedia(image_file);
                }
                status.setPossiblySensitive(is_possibly_sensitive);

                for (final long account_id : account_ids) {
                    // A very stupid workaround here, in order to send tweets
                    // contains asterisk symbol.
                    final Twitter twitter = getTwitterInstance(mContext, account_id, false,
                            !status.getStatus().contains("*"));
                    if (twitter != null) {
                        try {
                            result.add(new TwitterSingleResponse<twitter4j.Status>(account_id,
                                    twitter.updateStatus(status), null));
                        } catch (final TwitterException e) {
                            result.add(new TwitterSingleResponse<twitter4j.Status>(account_id, null, e));
                        }
                    }
                }
            } catch (final UpdateStatusException e) {
                for (final long account_id : account_ids) {
                    result.add(new TwitterSingleResponse<twitter4j.Status>(account_id, null, e));
                }
            }
            return result;
        }

        @Override
        protected void onCancelled() {
            saveDrafts(ListUtils.fromArray(account_ids));
            super.onCancelled();
        }

        @Override
        protected void onPostExecute(final List<TwitterSingleResponse<twitter4j.Status>> result) {

            boolean succeed = true;
            Exception exception = null;
            final List<Long> failed_account_ids = new ArrayList<Long>();

            for (final TwitterSingleResponse<twitter4j.Status> response : result) {
                if (response.data == null) {
                    succeed = false;
                    failed_account_ids.add(response.account_id);
                    if (exception == null) {
                        exception = response.exception;
                    }
                }
            }
            if (succeed) {
                Toast.makeText(mContext, R.string.send_successfully, Toast.LENGTH_SHORT).show();
                if (image_uri != null && delete_image) {
                    final String path = getImagePathFromUri(mContext, image_uri);
                    if (path != null) {
                        new File(path).delete();
                    }
                }
            } else {
                // If the status is a duplicate, there's no need to save it to
                // drafts.
                if (exception instanceof TwitterException
                        && ((TwitterException) exception).getErrorMessages() != null
                        && ((TwitterException) exception).getErrorMessages().length > 0
                        && ((TwitterException) exception).getErrorMessages()[0].getCode() == 187) {
                    Utils.showErrorToast(mContext, mContext.getString(R.string.status_is_duplicate), false);
                } else {
                    saveDrafts(failed_account_ids);
                    showErrorToast(R.string.sending_status, exception, true);
                }
            }
            super.onPostExecute(result);
            if (mPreferences.getBoolean(PREFERENCE_KEY_REFRESH_AFTER_TWEET, false)) {
                refreshAll();
            }
        }

        private void saveDrafts(final List<Long> account_ids) {
            final ContentValues values = new ContentValues();
            values.put(Drafts.ACCOUNT_IDS, ListUtils.toString(account_ids, ';', false));
            values.put(Drafts.IN_REPLY_TO_STATUS_ID, in_reply_to);
            values.put(Drafts.TEXT, content);
            if (image_uri != null) {
                values.put(Drafts.IS_IMAGE_ATTACHED, !delete_image);
                values.put(Drafts.IS_PHOTO_ATTACHED, delete_image);
                values.put(Drafts.IMAGE_URI, parseString(image_uri));
            }
            mResolver.insert(Drafts.CONTENT_URI, values);
            final String title = mContext.getString(R.string.tweet_not_sent);
            final String message = mContext.getString(R.string.tweet_not_sent_summary);
            final Intent intent = new Intent(INTENT_ACTION_DRAFTS);
            final Notification notification = buildNotification(title, message, R.drawable.ic_stat_tweet, intent,
                    null);
            mNotificationManager.notify(NOTIFICATION_ID_DRAFTS, notification);
        }

        class ImageUploaderNotFoundException extends UpdateStatusException {
            private static final long serialVersionUID = 1041685850011544106L;

            public ImageUploaderNotFoundException() {
                super(R.string.error_message_image_uploader_not_found);
            }
        }

        class ImageUploadException extends UpdateStatusException {
            private static final long serialVersionUID = 8596614696393917525L;

            public ImageUploadException() {
                super(R.string.error_message_image_upload_failed);
            }
        }

        class StatusTooLongException extends UpdateStatusException {
            private static final long serialVersionUID = -6469920130856384219L;

            public StatusTooLongException() {
                super(R.string.error_message_status_too_long);
            }
        }

        class TweetShortenerNotFoundException extends UpdateStatusException {
            private static final long serialVersionUID = -7262474256595304566L;

            public TweetShortenerNotFoundException() {
                super(R.string.error_message_tweet_shortener_not_found);
            }
        }

        class TweetShortenException extends UpdateStatusException {
            private static final long serialVersionUID = 3075877185536740034L;

            public TweetShortenException() {
                super(R.string.error_message_tweet_shorten_failed);
            }
        }

        class UpdateStatusException extends Exception {
            private static final long serialVersionUID = -1267218921727097910L;

            public UpdateStatusException(final int message) {
                super(mContext.getString(message));
            }
        }
    }

    class UpdateUserListDetailsTask extends ManagedAsyncTask<Void, Void, SingleResponse<UserList>> {

        private final long account_id;

        private final int list_id;

        private final boolean is_public;
        private final String name, description;

        public UpdateUserListDetailsTask(final long account_id, final int list_id, final boolean is_public,
                final String name, final String description) {
            super(mContext, mAsyncTaskManager);
            this.account_id = account_id;
            this.name = name;
            this.list_id = list_id;
            this.is_public = is_public;
            this.description = description;
        }

        @Override
        protected SingleResponse<UserList> doInBackground(final Void... params) {

            final Twitter twitter = getTwitterInstance(mContext, account_id, false);
            if (twitter != null) {
                try {
                    final UserList user = twitter.updateUserList(list_id, name, is_public, description);
                    return new TwitterSingleResponse<UserList>(account_id, user, null);
                } catch (final TwitterException e) {
                    return new TwitterSingleResponse<UserList>(account_id, null, e);
                }
            }
            return new TwitterSingleResponse<UserList>(account_id, null, null);
        }

        @Override
        protected void onPostExecute(final SingleResponse<UserList> result) {
            final Intent intent = new Intent(BROADCAST_USER_LIST_DETAILS_UPDATED);
            intent.putExtra(INTENT_KEY_LIST_ID, list_id);
            if (result != null && result.data != null && result.data.getId() > 0) {
                Toast.makeText(mContext, R.string.profile_update_successfully, Toast.LENGTH_SHORT).show();
                intent.putExtra(INTENT_KEY_SUCCEED, true);
            } else {
                showErrorToast(R.string.updating_details, result.exception, true);
            }
            mContext.sendBroadcast(intent);
            super.onPostExecute(result);
        }

    }

}