private SingleResponse<ParcelableDirectMessage> sendDirectMessage(
      final NotificationCompat.Builder builder,
      final long accountId,
      final long recipientId,
      final String text,
      final String imageUri) {
    final Twitter twitter = TwitterAPIFactory.getTwitterInstance(this, accountId, true, true);
    final TwitterUpload twitterUpload =
        TwitterAPIFactory.getTwitterInstance(this, accountId, true, true, TwitterUpload.class);
    if (twitter == null || twitterUpload == null) return SingleResponse.getInstance();
    try {
      final ParcelableDirectMessage directMessage;
      if (imageUri != null) {
        final String path = getImagePathFromUri(this, Uri.parse(imageUri));
        if (path == null) throw new FileNotFoundException();
        final BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, o);
        final File file = new File(path);
        BitmapUtils.downscaleImageIfNeeded(file, 100);
        final ContentLengthInputStream is = new ContentLengthInputStream(file);
        is.setReadListener(
            new MessageMediaUploadListener(this, mNotificationManager, builder, text));
        //                final MediaUploadResponse uploadResp = twitter.uploadMedia(file.getName(),
        // is, o.outMimeType);
        final MediaUploadResponse uploadResp = twitterUpload.uploadMedia(file);
        directMessage =
            new ParcelableDirectMessage(
                twitter.sendDirectMessage(recipientId, text, uploadResp.getId()), accountId, true);
        if (!file.delete()) {
          Log.d(LOGTAG, String.format("unable to delete %s", path));
        }
      } else {
        directMessage =
            new ParcelableDirectMessage(
                twitter.sendDirectMessage(recipientId, text), accountId, true);
      }
      Utils.setLastSeen(this, recipientId, System.currentTimeMillis());

      return SingleResponse.getInstance(directMessage);
    } catch (final IOException e) {
      return SingleResponse.getInstance(e);
    } catch (final TwitterException e) {
      return SingleResponse.getInstance(e);
    }
  }
  private void sendMessage(long accountId, long recipientId, String text, String imageUri) {
    if (accountId <= 0 || recipientId <= 0 || isEmpty(text)) return;
    final String title = getString(R.string.sending_direct_message);
    final Builder builder = new Builder(this);
    builder.setSmallIcon(R.drawable.ic_stat_send);
    builder.setProgress(100, 0, true);
    builder.setTicker(title);
    builder.setContentTitle(title);
    builder.setContentText(text);
    builder.setOngoing(true);
    final Notification notification = builder.build();
    startForeground(NOTIFICATION_ID_SEND_DIRECT_MESSAGE, notification);
    final SingleResponse<ParcelableDirectMessage> result =
        sendDirectMessage(builder, accountId, recipientId, text, imageUri);

    if (result.getData() != null && result.getData().id > 0) {
      final ContentValues values = ContentValuesCreator.createDirectMessage(result.getData());
      final String delete_where =
          DirectMessages.ACCOUNT_ID
              + " = "
              + accountId
              + " AND "
              + DirectMessages.MESSAGE_ID
              + " = "
              + result.getData().id;
      mResolver.delete(DirectMessages.Outbox.CONTENT_URI, delete_where, null);
      mResolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
      showOkMessage(R.string.direct_message_sent, false);

    } else {
      final ContentValues values = createMessageDraft(accountId, recipientId, text, imageUri);
      mResolver.insert(Drafts.CONTENT_URI, values);
      showErrorMessage(R.string.action_sending_direct_message, result.getException(), true);
    }
    stopForeground(false);
    mNotificationManager.cancel(NOTIFICATION_ID_SEND_DIRECT_MESSAGE);
  }
  private List<SingleResponse<ParcelableStatus>> updateStatus(
      final Builder builder, final ParcelableStatusUpdate statusUpdate) {
    final ArrayList<ContentValues> hashTagValues = new ArrayList<>();
    final Collection<String> hashTags = extractor.extractHashtags(statusUpdate.text);
    for (final String hashTag : hashTags) {
      final ContentValues values = new ContentValues();
      values.put(CachedHashtags.NAME, hashTag);
      hashTagValues.add(values);
    }
    final boolean hasEasterEggTriggerText = statusUpdate.text.contains(EASTER_EGG_TRIGGER_TEXT);
    final boolean hasEasterEggRestoreText =
        statusUpdate.text.contains(EASTER_EGG_RESTORE_TEXT_PART1)
            && statusUpdate.text.contains(EASTER_EGG_RESTORE_TEXT_PART2)
            && statusUpdate.text.contains(EASTER_EGG_RESTORE_TEXT_PART3);
    boolean mentionedHondaJOJO = false, notReplyToOther = false;
    mResolver.bulkInsert(
        CachedHashtags.CONTENT_URI, hashTagValues.toArray(new ContentValues[hashTagValues.size()]));

    final List<SingleResponse<ParcelableStatus>> results = new ArrayList<>();

    if (statusUpdate.accounts.length == 0) return Collections.emptyList();

    try {
      if (mUseUploader && mUploader == null) throw new UploaderNotFoundException(this);
      if (mUseShortener && mShortener == null) throw new ShortenerNotFoundException(this);

      final boolean hasMedia = statusUpdate.media != null && statusUpdate.media.length > 0;

      final String overrideStatusText;
      if (mUseUploader && mUploader != null && hasMedia) {
        final MediaUploadResult uploadResult;
        try {
          mUploader.waitForService();
          uploadResult =
              mUploader.upload(
                  statusUpdate, UploaderMediaItem.getFromStatusUpdate(this, statusUpdate));
        } catch (final Exception e) {
          throw new UploadException(this);
        } finally {
          mUploader.unbindService();
        }
        if (mUseUploader && hasMedia && uploadResult == null) throw new UploadException(this);
        if (uploadResult.error_code != 0) throw new UploadException(uploadResult.error_message);
        overrideStatusText = getImageUploadStatus(this, uploadResult.media_uris, statusUpdate.text);
      } else {
        overrideStatusText = null;
      }

      final String unShortenedText =
          isEmpty(overrideStatusText) ? statusUpdate.text : overrideStatusText;

      final boolean shouldShorten =
          mValidator.getTweetLength(unShortenedText) > mValidator.getMaxTweetLength();
      final String shortenedText;
      if (shouldShorten) {
        if (mUseShortener) {
          final StatusShortenResult shortenedResult;
          mShortener.waitForService();
          try {
            shortenedResult = mShortener.shorten(statusUpdate, unShortenedText);
          } catch (final Exception e) {
            throw new ShortenException(this);
          } finally {
            mShortener.unbindService();
          }
          if (shortenedResult == null || shortenedResult.shortened == null)
            throw new ShortenException(this);
          shortenedText = shortenedResult.shortened;
        } else throw new StatusTooLongException(this);
      } else {
        shortenedText = unShortenedText;
      }
      if (statusUpdate.media != null) {
        for (final ParcelableMediaUpdate media : statusUpdate.media) {
          final String path = getImagePathFromUri(this, Uri.parse(media.uri));
          final File file = path != null ? new File(path) : null;
          if (!mUseUploader && file != null && file.exists()) {
            BitmapUtils.downscaleImageIfNeeded(file, 95);
          }
        }
      }
      for (final ParcelableAccount account : statusUpdate.accounts) {
        final Twitter twitter =
            TwitterAPIFactory.getTwitterInstance(this, account.account_id, true, true);
        final TwitterUpload upload =
            TwitterAPIFactory.getTwitterInstance(
                this, account.account_id, true, true, TwitterUpload.class);
        final StatusUpdate status = new StatusUpdate(shortenedText);
        status.inReplyToStatusId(statusUpdate.in_reply_to_status_id);
        if (statusUpdate.location != null) {
          status.location(ParcelableLocation.toGeoLocation(statusUpdate.location));
        }
        if (!mUseUploader && hasMedia) {
          final BitmapFactory.Options o = new BitmapFactory.Options();
          o.inJustDecodeBounds = true;
          final long[] mediaIds = new long[statusUpdate.media.length];
          ContentLengthInputStream is = null;
          try {
            for (int i = 0, j = mediaIds.length; i < j; i++) {
              final ParcelableMediaUpdate media = statusUpdate.media[i];
              final String path = getImagePathFromUri(this, Uri.parse(media.uri));
              if (path == null) throw new FileNotFoundException();
              BitmapFactory.decodeFile(path, o);
              final File file = new File(path);
              is = new ContentLengthInputStream(file);
              is.setReadListener(
                  new StatusMediaUploadListener(this, mNotificationManager, builder, statusUpdate));
              final ContentType contentType;
              if (TextUtils.isEmpty(o.outMimeType)) {
                contentType = ContentType.parse("image/*");
              } else {
                contentType = ContentType.parse(o.outMimeType);
              }
              final MediaUploadResponse uploadResp =
                  upload.uploadMedia(
                      new FileTypedData(is, file.getName(), file.length(), contentType));
              mediaIds[i] = uploadResp.getId();
            }
          } catch (final FileNotFoundException e) {
            Log.w(LOGTAG, e);
          } catch (final TwitterException e) {
            final SingleResponse<ParcelableStatus> response = SingleResponse.getInstance(e);
            results.add(response);
            continue;
          } finally {
            IoUtils.closeSilently(is);
          }
          status.mediaIds(mediaIds);
        }
        status.possiblySensitive(statusUpdate.is_possibly_sensitive);

        if (twitter == null) {
          results.add(SingleResponse.<ParcelableStatus>getInstance(new NullPointerException()));
          continue;
        }
        try {
          final Status resultStatus = twitter.updateStatus(status);
          if (!mentionedHondaJOJO) {
            final UserMentionEntity[] entities = resultStatus.getUserMentionEntities();
            if (entities == null || entities.length == 0) {
              mentionedHondaJOJO = statusUpdate.text.contains("@" + HONDAJOJO_SCREEN_NAME);
            } else if (entities.length == 1 && entities[0].getId() == HONDAJOJO_ID) {
              mentionedHondaJOJO = true;
            }
            Utils.setLastSeen(this, entities, System.currentTimeMillis());
          }
          if (!notReplyToOther) {
            final long inReplyToUserId = resultStatus.getInReplyToUserId();
            if (inReplyToUserId <= 0 || inReplyToUserId == HONDAJOJO_ID) {
              notReplyToOther = true;
            }
          }
          final ParcelableStatus result =
              new ParcelableStatus(resultStatus, account.account_id, false);
          results.add(SingleResponse.getInstance(result));
        } catch (final TwitterException e) {
          final SingleResponse<ParcelableStatus> response = SingleResponse.getInstance(e);
          results.add(response);
        }
      }
    } catch (final UpdateStatusException e) {
      final SingleResponse<ParcelableStatus> response = SingleResponse.getInstance(e);
      results.add(response);
    }
    if (mentionedHondaJOJO) {
      triggerEasterEgg(notReplyToOther, hasEasterEggTriggerText, hasEasterEggRestoreText);
    }
    return results;
  }
  private void updateStatuses(ParcelableStatusUpdate... statuses) {
    final Builder builder = new Builder(this);
    startForeground(
        NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(this, builder, 0, null));
    for (final ParcelableStatusUpdate item : statuses) {
      mNotificationManager.notify(
          NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(this, builder, 0, item));
      final ContentValues draftValues =
          ContentValuesCreator.createStatusDraft(
              item, ParcelableAccount.getAccountIds(item.accounts));
      final Uri draftUri = mResolver.insert(Drafts.CONTENT_URI, draftValues);
      final long draftId = ParseUtils.parseLong(draftUri.getLastPathSegment(), -1);
      mTwitter.addSendingDraftId(draftId);
      final List<SingleResponse<ParcelableStatus>> result = updateStatus(builder, item);
      boolean failed = false;
      Exception exception = null;
      final Expression where = Expression.equals(Drafts._ID, draftId);
      final List<Long> failedAccountIds =
          ListUtils.fromArray(ParcelableAccount.getAccountIds(item.accounts));

      for (final SingleResponse<ParcelableStatus> response : result) {

        if (response.getData() == null) {
          failed = true;
          if (exception == null) {
            exception = response.getException();
          }
        } else if (response.getData().account_id > 0) {
          failedAccountIds.remove(response.getData().account_id);
          // spice
          if (response.getData().media == null) {
            SpiceProfilingUtil.log(
                response.getData().id
                    + ",Tweet,"
                    + response.getData().account_id
                    + ","
                    + response.getData().in_reply_to_user_id
                    + ","
                    + response.getData().in_reply_to_status_id);
            SpiceProfilingUtil.profile(
                this.getBaseContext(),
                response.getData().account_id,
                response.getData().id
                    + ",Tweet,"
                    + response.getData().account_id
                    + ","
                    + response.getData().in_reply_to_user_id
                    + ","
                    + response.getData().in_reply_to_status_id);
          } else
            for (final ParcelableMedia spiceMedia : response.getData().media) {
              SpiceProfilingUtil.log(
                  response.getData().id
                      + ",Media,"
                      + response.getData().account_id
                      + ","
                      + response.getData().in_reply_to_user_id
                      + ","
                      + response.getData().in_reply_to_status_id
                      + ","
                      + spiceMedia.media_url
                      + ","
                      + TypeMappingUtil.getMediaType(spiceMedia.type));
              SpiceProfilingUtil.profile(
                  this.getBaseContext(),
                  response.getData().account_id,
                  response.getData().id
                      + ",Media,"
                      + response.getData().account_id
                      + ","
                      + response.getData().in_reply_to_user_id
                      + ","
                      + response.getData().in_reply_to_status_id
                      + ","
                      + spiceMedia.media_url
                      + ","
                      + TypeMappingUtil.getMediaType(spiceMedia.type));
            }
          // end
        }
      }

      if (result.isEmpty()) {
        showErrorMessage(
            R.string.action_updating_status, getString(R.string.no_account_selected), false);
      } else if (failed) {
        // If the status is a duplicate, there's no need to save it to
        // drafts.
        if (exception instanceof TwitterException
            && ((TwitterException) exception).getErrorCode()
                == StatusCodeMessageUtils.STATUS_IS_DUPLICATE) {
          showErrorMessage(getString(R.string.status_is_duplicate), false);
        } else {
          final ContentValues accountIdsValues = new ContentValues();
          accountIdsValues.put(
              Drafts.ACCOUNT_IDS, ListUtils.toString(failedAccountIds, ',', false));
          mResolver.update(Drafts.CONTENT_URI, accountIdsValues, where.getSQL(), null);
          showErrorMessage(R.string.action_updating_status, exception, true);
          final ContentValues notifValues = new ContentValues();
          notifValues.put(BaseColumns._ID, draftId);
          mResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, notifValues);
        }
      } else {
        showOkMessage(R.string.status_updated, false);
        mResolver.delete(Drafts.CONTENT_URI, where.getSQL(), null);
        if (item.media != null) {
          for (final ParcelableMediaUpdate media : item.media) {
            final String path = getImagePathFromUri(this, Uri.parse(media.uri));
            if (path != null) {
              if (!new File(path).delete()) {
                Log.d(LOGTAG, String.format("unable to delete %s", path));
              }
            }
          }
        }
      }
      mTwitter.removeSendingDraftId(draftId);
      if (mPreferences.getBoolean(KEY_REFRESH_AFTER_TWEET, false)) {
        mHandler.post(
            new Runnable() {
              @Override
              public void run() {
                mTwitter.refreshAll();
              }
            });
      }
    }
    stopForeground(false);
    mNotificationManager.cancel(NOTIFICATION_ID_UPDATE_STATUS);
  }