// Returns the task if we started the task or the task is already started.
  private Future<?> startTaskIfNeeded(int index, int which) {
    if (index < mActiveStart || index >= mActiveEnd) return null;

    ImageEntry entry = mImageCache.get(getVersion(index));
    if (entry == null) return null;

    if (which == BIT_SCREEN_NAIL && entry.screenNailTask != null) {
      return entry.screenNailTask;
    } else if (which == BIT_FULL_IMAGE && entry.fullImageTask != null) {
      return entry.fullImageTask;
    }

    MediaItem item = mData[index % DATA_CACHE_SIZE];
    Utils.assertTrue(item != null);

    if (which == BIT_SCREEN_NAIL && (entry.requestedBits & BIT_SCREEN_NAIL) == 0) {
      entry.requestedBits |= BIT_SCREEN_NAIL;
      entry.screenNailTask =
          mThreadPool.submit(
              new ScreenNailJob(item), new ScreenNailListener(item.getDataVersion()));
      // request screen nail
      return entry.screenNailTask;
    }
    if (which == BIT_FULL_IMAGE
        && (entry.requestedBits & BIT_FULL_IMAGE) == 0
        && (item.getSupportedOperations() & MediaItem.SUPPORT_FULL_IMAGE) != 0) {
      entry.requestedBits |= BIT_FULL_IMAGE;
      entry.fullImageTask =
          mThreadPool.submit(
              item.requestLargeImage(), new FullImageListener(item.getDataVersion()));
      // request full image
      return entry.fullImageTask;
    }
    return null;
  }
  private void updateImageCache() {
    HashSet<Long> toBeRemoved = new HashSet<Long>(mImageCache.keySet());
    for (int i = mActiveStart; i < mActiveEnd; ++i) {
      MediaItem item = mData[i % DATA_CACHE_SIZE];
      long version = item == null ? MediaObject.INVALID_DATA_VERSION : item.getDataVersion();
      if (version == MediaObject.INVALID_DATA_VERSION) continue;
      ImageEntry entry = mImageCache.get(version);
      toBeRemoved.remove(version);
      if (entry != null) {
        if (Math.abs(i - mCurrentIndex) > 1) {
          if (entry.fullImageTask != null) {
            entry.fullImageTask.cancel();
            entry.fullImageTask = null;
          }
          entry.fullImage = null;
          entry.requestedBits &= ~BIT_FULL_IMAGE;
        }
      } else {
        entry = new ImageEntry();
        entry.rotation = item.getFullImageRotation();
        mImageCache.put(version, entry);
      }
    }

    // Clear the data and requests for ImageEntries outside the new window.
    for (Long version : toBeRemoved) {
      ImageEntry entry = mImageCache.remove(version);
      if (entry.fullImageTask != null) entry.fullImageTask.cancel();
      if (entry.screenNailTask != null) entry.screenNailTask.cancel();
    }
  }
 private long getVersion(int index) {
   if (index < 0 || index >= mSize) return VERSION_OUT_OF_RANGE;
   if (index >= mContentStart && index < mContentEnd) {
     MediaItem item = mData[index % DATA_CACHE_SIZE];
     if (item != null) return item.getDataVersion();
   }
   return MediaObject.INVALID_DATA_VERSION;
 }