public static <T extends List<Status>> T getStatusesWithQuoteData(
     Twitter twitter, @NonNull T list) throws TwitterException {
   LongSparseMap<Status> quotes = new LongSparseMap<>();
   // Phase 1: collect all statuses contains a status link, and put it in the map
   for (Status status : list) {
     if (status.isQuote()) continue;
     final UrlEntity[] entities = status.getUrlEntities();
     if (entities == null || entities.length <= 0) continue;
     // Seems Twitter will find last status link for quote target, so we search backward
     for (int i = entities.length - 1; i >= 0; i--) {
       final Matcher m = PATTERN_TWITTER_STATUS_LINK.matcher(entities[i].getExpandedUrl());
       if (!m.matches()) continue;
       final long def = -1;
       final long quoteId = NumberUtils.toLong(m.group(3), def);
       if (quoteId > 0) {
         quotes.put(quoteId, status);
       }
       break;
     }
   }
   // Phase 2: look up quoted tweets. Each lookup can fetch up to 100 tweets, so we split quote
   // ids into batches
   final long[] quoteIds = quotes.keys();
   for (int currentBulkIdx = 0, totalLength = quoteIds.length;
       currentBulkIdx < totalLength;
       currentBulkIdx += TWITTER_BULK_QUERY_COUNT) {
     final int currentBulkCount =
         Math.min(totalLength, currentBulkIdx + TWITTER_BULK_QUERY_COUNT) - currentBulkIdx;
     final long[] ids = new long[currentBulkCount];
     System.arraycopy(quoteIds, currentBulkIdx, ids, 0, currentBulkCount);
     // Lookup quoted statuses, then set each status into original status
     for (Status quoted : twitter.lookupStatuses(ids)) {
       final Set<Status> orig = quotes.get(quoted.getId());
       // This set shouldn't be null here, add null check to make inspector happy.
       if (orig == null) continue;
       for (Status status : orig) {
         Status.setQuotedStatus(status, quoted);
       }
     }
   }
   return list;
 }
  public ParcelableStatus(final Status orig, final long account_id, final boolean is_gap) {
    this.is_gap = is_gap;
    this.account_id = account_id;
    id = orig.getId();
    timestamp = getTime(orig.getCreatedAt());

    final Status retweeted = orig.getRetweetedStatus();
    final User retweet_user = retweeted != null ? orig.getUser() : null;
    is_retweet = orig.isRetweet();
    retweet_id = retweeted != null ? retweeted.getId() : -1;
    retweet_timestamp = retweeted != null ? getTime(retweeted.getCreatedAt()) : -1;
    retweeted_by_user_id = retweet_user != null ? retweet_user.getId() : -1;
    retweeted_by_user_name = retweet_user != null ? retweet_user.getName() : null;
    retweeted_by_user_screen_name = retweet_user != null ? retweet_user.getScreenName() : null;
    retweeted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(retweet_user);

    final Status quoted = orig.getQuotedStatus();
    final User quote_user = quoted != null ? orig.getUser() : null;
    is_quote = orig.isQuote();
    quote_id = quoted != null ? quoted.getId() : -1;
    quote_text_html = TwitterContentUtils.formatStatusText(orig);
    quote_text_plain = TwitterContentUtils.unescapeTwitterStatusText(orig.getText());
    quote_text_unescaped = HtmlEscapeHelper.toPlainText(quote_text_html);
    quote_timestamp = orig.getCreatedAt().getTime();
    quote_source = orig.getSource();
    quote_retweet_count = orig.getRetweetCount();
    quote_favorite_count = orig.getFavoriteCount();
    quote_reply_count = orig.getReplyCount();

    quoted_by_user_id = quote_user != null ? quote_user.getId() : -1;
    quoted_by_user_name = quote_user != null ? quote_user.getName() : null;
    quoted_by_user_screen_name = quote_user != null ? quote_user.getScreenName() : null;
    quoted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(quote_user);
    quoted_by_user_is_protected = quote_user != null && quote_user.isProtected();
    quoted_by_user_is_verified = quote_user != null && quote_user.isVerified();

    final Status status;
    if (quoted != null) {
      status = quoted;
    } else if (retweeted != null) {
      status = retweeted;
    } else {
      status = orig;
    }
    final User user = status.getUser();
    user_id = user.getId();
    user_name = user.getName();
    user_screen_name = user.getScreenName();
    user_profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
    user_is_protected = user.isProtected();
    user_is_verified = user.isVerified();
    user_is_following = user.isFollowing();
    text_html = TwitterContentUtils.formatStatusText(status);
    media = ParcelableMedia.fromStatus(status);
    quote_media = quoted != null ? ParcelableMedia.fromStatus(orig) : null;
    text_plain = TwitterContentUtils.unescapeTwitterStatusText(status.getText());
    retweet_count = status.getRetweetCount();
    favorite_count = status.getFavoriteCount();
    reply_count = status.getReplyCount();
    in_reply_to_name = TwitterContentUtils.getInReplyToName(retweeted != null ? retweeted : orig);
    in_reply_to_screen_name = (retweeted != null ? retweeted : orig).getInReplyToScreenName();
    in_reply_to_status_id = (retweeted != null ? retweeted : orig).getInReplyToStatusId();
    in_reply_to_user_id = (retweeted != null ? retweeted : orig).getInReplyToUserId();
    source = status.getSource();
    location = ParcelableLocation.fromGeoLocation(status.getGeoLocation());
    is_favorite = status.isFavorited();
    text_unescaped = HtmlEscapeHelper.toPlainText(text_html);
    my_retweet_id = retweeted_by_user_id == account_id ? id : status.getCurrentUserRetweet();
    is_possibly_sensitive = status.isPossiblySensitive();
    mentions = ParcelableUserMention.fromUserMentionEntities(status.getUserMentionEntities());
    card = ParcelableCardEntity.fromCardEntity(status.getCard(), account_id);
    place_full_name = getPlaceFullName(status.getPlace());
    card_name = card != null ? card.name : null;
  }
  private void storeStatus(
      long accountId,
      List<org.mariotaku.twidere.api.twitter.model.Status> statuses,
      long sinceId,
      long maxId,
      boolean notify,
      int loadItemLimit) {
    if (statuses == null || statuses.isEmpty() || accountId <= 0) {
      return;
    }
    final Uri uri = getDatabaseUri();
    final Context context = twitterWrapper.getContext();
    final ContentResolver resolver = context.getContentResolver();
    final boolean noItemsBefore = DataStoreUtils.getStatusCount(context, uri, accountId) <= 0;
    final ContentValues[] values = new ContentValues[statuses.size()];
    final long[] statusIds = new long[statuses.size()];
    long minId = -1;
    int minIdx = -1;
    boolean hasIntersection = false;
    for (int i = 0, j = statuses.size(); i < j; i++) {
      final org.mariotaku.twidere.api.twitter.model.Status status = statuses.get(i);
      values[i] = ContentValuesCreator.createStatus(status, accountId);
      values[i].put(Statuses.INSERTED_DATE, System.currentTimeMillis());
      final long id = status.getId();
      if (sinceId > 0 && id <= sinceId) {
        hasIntersection = true;
      }
      if (minId == -1 || id < minId) {
        minId = id;
        minIdx = i;
      }
      statusIds[i] = id;
    }
    // Delete all rows conflicting before new data inserted.
    final Expression accountWhere = Expression.equals(Statuses.ACCOUNT_ID, accountId);
    final Expression statusWhere =
        Expression.in(new Columns.Column(Statuses.STATUS_ID), new RawItemArray(statusIds));
    final String countWhere = Expression.and(accountWhere, statusWhere).getSQL();
    final String[] projection = {SQLFunctions.COUNT()};
    final int rowsDeleted;
    final Cursor countCur = resolver.query(uri, projection, countWhere, null, null);
    try {
      if (countCur != null && countCur.moveToFirst()) {
        rowsDeleted = countCur.getInt(0);
      } else {
        rowsDeleted = 0;
      }
    } finally {
      Utils.closeSilently(countCur);
    }

    // BEGIN HotMobi
    final RefreshEvent event = RefreshEvent.create(context, statusIds, getTimelineType());
    HotMobiLogger.getInstance(context).log(accountId, event);
    // END HotMobi

    // Insert a gap.
    final boolean deletedOldGap = rowsDeleted > 0 && ArrayUtils.contains(statusIds, maxId);
    final boolean noRowsDeleted = rowsDeleted == 0;
    final boolean insertGap =
        minId > 0 && (noRowsDeleted || deletedOldGap) && !noItemsBefore && !hasIntersection;
    if (insertGap && minIdx != -1) {
      values[minIdx].put(Statuses.IS_GAP, true);
    }
    // Insert previously fetched items.
    final Uri insertUri = UriUtils.appendQueryParameters(uri, QUERY_PARAM_NOTIFY, notify);
    ContentResolverUtils.bulkInsert(resolver, insertUri, values);
  }