private void parseCommentsJSON(InputStream in) throws IOException, JsonParseException {
    int insertedCommentIndex;
    String genericListingError = "Not a comments listing";
    try {
      Listing[] listings = mObjectMapper.readValue(in, Listing[].class);

      // listings[0] is a thread Listing for the OP.
      // process same as a thread listing more or less

      Assert.assertEquals(Constants.JSON_LISTING, listings[0].getKind(), genericListingError);

      // Save modhash, ignore "after" and "before" which are meaningless in this context (and
      // probably null)
      ListingData threadListingData = listings[0].getData();
      if (StringUtils.isEmpty(threadListingData.getModhash())) mSettings.setModhash(null);
      else mSettings.setModhash(threadListingData.getModhash());

      if (Constants.LOGGING)
        Log.d(TAG, "Successfully got OP listing[0]: modhash " + mSettings.getModhash());
      ThingListing threadThingListing = threadListingData.getChildren()[0];
      Assert.assertEquals(Constants.THREAD_KIND, threadThingListing.getKind(), genericListingError);
      // listings[1] is a comment Listing for the comments
      ListingData commentListingData = listings[1].getData();
      if (isInsertingEntireThread()) {
        parseOP(
            threadThingListing.getData(),
            commentListingData.getChildren().length == 0
                ? null
                : commentListingData.getChildren()[0].getData());
        insertedCommentIndex = 0; // we just inserted the OP into position 0
        if (!StringUtils.isEmpty(mJumpToCommentId) && mJumpToCommentContext > 0) {
          // If viewing context, then the 'first' item will be the context-viewing warning.
          insertedCommentIndex++;
        }

        // at this point we've started displaying comments, so disable the loading screen
        disableLoadingScreenKeepProgress();
      } else {
        insertedCommentIndex = mPositionOffset - 1; // -1 because we +1 for the first comment
      }

      // Go through the children and get the ThingInfos
      for (ThingListing commentThingListing : commentListingData.getChildren()) {
        // insert the comment and its replies, prefix traversal order
        insertedCommentIndex =
            insertNestedComment(commentThingListing, 0, insertedCommentIndex + 1);
      }

      mProcessCommentsTask.mergeLowPriorityListToMainList();

    } catch (Exception ex) {
      if (Constants.LOGGING) Log.e(TAG, "parseCommentsJSON", ex);
    }
  }
 private boolean isHasJumpTarget() {
   return !StringUtils.isEmpty(mJumpToCommentId);
 }
  // XXX: maxComments is unused for now
  public Boolean doInBackground(Integer... maxComments) {
    HttpEntity entity = null;
    try {
      StringBuilder sb = new StringBuilder(Constants.REDDIT_BASE_URL);
      if (mSubreddit != null) {
        sb.append("/r/").append(mSubreddit.trim());
      }
      if (mMoreChildrenId != null && mMoreChildrenId.length() > 0) {
        sb.append("/comments/")
            .append(mThreadId)
            .append("/z/")
            .append(mMoreChildrenId)
            .append("/.json?")
            .append(mSettings.getCommentsSortByUrl());
        // Loading more with context makes no sense
      } else {
        sb.append("/comments/")
            .append(mThreadId)
            .append("/.json?")
            .append(mSettings.getCommentsSortByUrl());
        if (!StringUtils.isEmpty(mJumpToCommentId) && mJumpToCommentContext > 0) {
          sb.append("&comment=")
              .append(mJumpToCommentId)
              .append("&context=")
              .append(mJumpToCommentContext);
        }
      }

      String url = sb.toString();
      if (Constants.LOGGING) Log.d(TAG, "Loading comments from URL: " + url);
      InputStream in = null;
      boolean currentlyUsingCache = false;

      if (Constants.USE_COMMENTS_CACHE) {
        try {
          if (CacheInfo.checkFreshThreadCache(mActivity.getApplicationContext())
              && url.equals(CacheInfo.getCachedThreadUrl(mActivity.getApplicationContext()))) {
            in = mActivity.openFileInput(Constants.FILENAME_THREAD_CACHE);
            mContentLength = mActivity.getFileStreamPath(Constants.FILENAME_THREAD_CACHE).length();
            currentlyUsingCache = true;
            if (Constants.LOGGING) Log.d(TAG, "Using cached thread JSON, length=" + mContentLength);
          }
        } catch (Exception cacheEx) {
          if (Constants.LOGGING) Log.w(TAG, "skip cache", cacheEx);
        }
      }

      // If we couldn't use the cache, then do HTTP request
      if (!currentlyUsingCache) {
        HttpGet request = new HttpGet(url);
        HttpResponse response = mClient.execute(request);

        // Read the header to get Content-Length since entity.getContentLength() returns -1
        Header contentLengthHeader = response.getFirstHeader("Content-Length");
        if (contentLengthHeader != null) {
          mContentLength = Long.valueOf(contentLengthHeader.getValue());
          if (Constants.LOGGING) Log.d(TAG, "Content length: " + mContentLength);
        } else {
          mContentLength = -1;
          if (Constants.LOGGING) Log.d(TAG, "Content length: UNAVAILABLE");
        }

        entity = response.getEntity();
        in = entity.getContent();

        if (Constants.USE_COMMENTS_CACHE) {
          in =
              CacheInfo.writeThenRead(
                  mActivity.getApplicationContext(), in, Constants.FILENAME_THREAD_CACHE);
          try {
            CacheInfo.setCachedThreadUrl(mActivity.getApplicationContext(), url);
          } catch (IOException e) {
            if (Constants.LOGGING) Log.e(TAG, "error on setCachedThreadId", e);
          }
        }
      }

      // setup a special InputStream to report progress
      ProgressInputStream pin = new ProgressInputStream(in, mContentLength);
      pin.addPropertyChangeListener(this);

      parseCommentsJSON(pin);
      if (Constants.LOGGING) Log.d(TAG, "parseCommentsJSON completed");

      pin.close();
      in.close();

      return true;

    } catch (Exception e) {
      if (Constants.LOGGING) Log.e(TAG, "DownloadCommentsTask", e);
    } finally {
      if (entity != null) {
        try {
          entity.consumeContent();
        } catch (Exception e2) {
          if (Constants.LOGGING) Log.e(TAG, "entity.consumeContent()", e2);
        }
      }
    }
    return false;
  }