// called if the file is not found on the file system
    @Override
    public void notFound() {
      ApptentiveLog.v("ApptentiveAttachmentLoader notFound: " + uri);
      if (mIsCancelled) {
        return;
      }
      ImageView imageView = getImageView();

      if (imageView == null || this != imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
        return;
      }

      if (isAnotherQueuedOrRunningWithSameUrl() != null) {
        if (duplicateDownloads.containsKey(uri)) {
          ArrayList<LoaderRequest> arr = duplicateDownloads.get(uri);
          arr.add(this);
          duplicateDownloads.put(uri, arr);
        } else {
          ArrayList<LoaderRequest> arr = new ArrayList<LoaderRequest>();
          arr.add(this);
          duplicateDownloads.put(uri, arr);
        }
      } else {
        // check if this imageView is being used with a different URL, if so
        // cancel the other one.
        int queuedIndex = indexOfQueuedDownloadWithDifferentURL();
        int downloadIndex = indexOfDownloadWithDifferentURL();
        while (queuedIndex != -1) {
          queuedDownLoaderRequests.remove(queuedIndex);
          ApptentiveLog.v("ApptentiveAttachmentLoader notFound(Removing): " + uri);
          queuedIndex = indexOfQueuedDownloadWithDifferentURL();
        }
        if (downloadIndex != -1) {
          LoaderRequest runningLoaderRequest = runningDownLoaderRequests.get(downloadIndex);
          ApptentiveDownloaderTask downloadTask = runningLoaderRequest.getDrawableDownloaderTask();
          if (downloadTask != null) {
            downloadTask.cancel(true);
            ApptentiveLog.v("ApptentiveAttachmentLoader notFound(Cancelling): " + uri);
          }
        }

        if (!(isBeingDownloaded() || isQueuedForDownload())) {
          if (runningDownLoaderRequests.size() >= maxDownloads) {
            ApptentiveLog.v("ApptentiveAttachmentLoader notFound(Queuing): " + uri);
            queuedDownLoaderRequests.add(this);
          } else {
            ApptentiveLog.v("ApptentiveAttachmentLoader notFound(Downloading): " + uri);
            doDownload();
          }
        }
      }
    }
    public void load() {
      ImageView imageView = mImageViewRef.get();
      if (imageView != null) {
        ApptentiveLog.v("ApptentiveAttachmentLoader load requested:" + uri);
        ApptentiveLog.v("ApptentiveAttachmentLoader load requested on:" + imageView.toString());

        // Handle the duplicate requests on the same grid item view
        LoaderRequest oldLoaderRequest = (LoaderRequest) imageView.getTag(DRAWABLE_DOWNLOAD_TAG);
        if (oldLoaderRequest != null) {
          // If old request on the same view also loads from the same source, cancel the current one
          if (oldLoaderRequest.getUrl().equals(uri)) {
            ApptentiveLog.v("ApptentiveAttachmentLoader load new request denied:" + uri);
            return;
          }
          // If old request on the same view loads from different source, cancel the old one
          oldLoaderRequest.cancel();
        }

        if (TextUtils.isEmpty(uri)) {
          ApptentiveLog.v("ApptentiveAttachmentLoader loadDrawable(clear)");
          loadDrawable(null);
          imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
          return;
        }

        Bitmap cachedBitmap =
            (bLoadImage)
                ? (Bitmap)
                    bitmapMemoryCache.getObjectFromCache(
                        ImageMemoryCache.generateMemoryCacheEntryKey(
                            uri, imageViewWidth, imageViewHeight))
                : null;
        if (cachedBitmap != null) {
          mWasDownloaded = false;

          ApptentiveLog.v("ApptentiveAttachmentLoader loadDrawable(found in cache)");
          loadDrawable(cachedBitmap);
          imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
        } else {
          imageView.setTag(DRAWABLE_DOWNLOAD_TAG, this);
          if (bLoadImage) {
            loadImageFromDisk(imageView);
          } else {
            loadAttachmentFromDisk(imageView);
          }
        }
      }
    }
    // called if there is an error with the download
    @Override
    public void onDownloadError() {
      ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadError: " + uri);
      runningDownLoaderRequests.remove(this);
      filesBeingDownloaded.remove(diskCacheFilePath);
      ImageView imageView = getImageView();
      mWasDownloaded = true;
      if (imageView != null) {
        if (loadingTaskCallback != null) {
          loadingTaskCallback.onDownloadProgress(-1);
        }
      }

      if (imageView != null && this == imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
        imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
      }

      ArrayList<LoaderRequest> duplicates = duplicateDownloads.get(uri);
      if (duplicates != null) {
        duplicates.remove(this);
        if (duplicates.size() > 0) {
          duplicateDownloads.put(uri, duplicates);
        } else {
          duplicateDownloads.remove(uri);
        }
        for (LoaderRequest dup : duplicates) {
          ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadError (dup): " + dup.uri);
          // load the image.
          if (dup != null
              && dup.getImageView() != null
              && dup.getImageView().getTag(DRAWABLE_DOWNLOAD_TAG) == dup) {
            duplicates.remove(0);
            if (duplicates.size() > 0) {
              duplicateDownloads.put(uri, duplicates);
            } else {
              duplicateDownloads.remove(uri);
            }
            dup.doDownload();
            return;
          }
        }
      }

      if (!queuedDownLoaderRequests.isEmpty()) {
        LoaderRequest d = queuedDownLoaderRequests.remove(0);
        d.doDownload();
      }
    }
 public void doDownload() {
   if (mIsCancelled) {
     // if the download has been cancelled, do not download
     // this image, but start the next one
     if (!queuedDownLoaderRequests.isEmpty()
         && runningDownLoaderRequests.size() < maxDownloads) {
       LoaderRequest d = queuedDownLoaderRequests.remove(0);
       d.doDownload();
     }
     return;
   }
   ImageView imageView = mImageViewRef.get();
   if (imageView != null
       && imageView.getTag(DRAWABLE_DOWNLOAD_TAG) == this
       && URLUtil.isNetworkUrl(uri)) {
     mDrawableDownloaderTask = new ApptentiveDownloaderTask(imageView, this);
     try {
       ApptentiveLog.v("ApptentiveAttachmentLoader doDownload: " + uri);
       // Conversation token is needed if the download url is a redirect link from an Apptentive
       // endpoint
       String conversationToken =
           ApptentiveInternal.getInstance().getApptentiveConversationToken();
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
         mDrawableDownloaderTask.executeOnExecutor(
             AsyncTask.THREAD_POOL_EXECUTOR, uri, diskCacheFilePath, conversationToken);
       } else {
         mDrawableDownloaderTask.execute(uri, diskCacheFilePath, conversationToken);
       }
     } catch (RejectedExecutionException e) {
     }
     runningDownLoaderRequests.add(this);
     filesBeingDownloaded.add(diskCacheFilePath);
   }
 }
    // called when the download has completed
    @Override
    public void onDownloadComplete() {
      ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadComplete: " + uri);

      runningDownLoaderRequests.remove(this);
      filesBeingDownloaded.remove(diskCacheFilePath);
      mWasDownloaded = true;

      ImageView imageView = mImageViewRef.get();
      if (imageView != null && this == imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
        if (!bLoadImage) {
          imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
          if (loadingTaskCallback != null) {
            loadingTaskCallback.onLoaded(imageView, pos, null);
          }
        } else {
          loadImageFromDisk(getImageView());
        }
      }

      ArrayList<LoaderRequest> duplicates = duplicateDownloads.get(uri);
      if (duplicates != null) {
        for (LoaderRequest dup : duplicates) {
          ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadComplete (dup): " + dup.uri);
          // load the image.
          if (dup != null
              && dup.getImageView() != null
              && dup.getImageView().getTag(DRAWABLE_DOWNLOAD_TAG) == dup) {
            if (!dup.isLoadingImage()) {
              dup.getImageView().setTag(DRAWABLE_DOWNLOAD_TAG, null);
              if (dup.getLoaderCallback() != null) {
                dup.getLoaderCallback().onLoaded(dup.getImageView(), dup.pos, null);
              }
            } else {
              dup.loadImageFromDisk(dup.getImageView());
            }
          }
        }
        duplicateDownloads.remove(uri);
      }

      if (!queuedDownLoaderRequests.isEmpty()) {
        LoaderRequest d = queuedDownLoaderRequests.remove(0);
        d.doDownload();
      }
    }
 private void loadDrawable(Bitmap d, boolean animate) {
   ApptentiveLog.v("ApptentiveAttachmentLoader loadDrawable");
   ImageView imageView = getImageView();
   if (imageView != null) {
     if (loadingTaskCallback != null) {
       loadingTaskCallback.onLoaded(imageView, pos, d);
     }
   }
 }
    // called if the download is cancelled
    @Override
    public void onDownloadCancel() {
      mIsCancelled = true;
      ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadCancel: " + uri);
      runningDownLoaderRequests.remove(this);
      filesBeingDownloaded.remove(diskCacheFilePath);

      ImageView imageView = mImageViewRef.get();
      if (imageView != null && this == imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
        imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
      }

      ArrayList<LoaderRequest> duplicates = duplicateDownloads.get(uri);
      if (duplicates != null) {
        duplicates.remove(this);
        if (duplicates.size() > 0) {
          duplicateDownloads.put(uri, duplicates);
        } else {
          duplicateDownloads.remove(uri);
        }
        for (LoaderRequest dup : duplicates) {
          // start next download task in the duplicate queue
          if (dup != null
              && dup.getImageView() != null
              && dup.getImageView().getTag(DRAWABLE_DOWNLOAD_TAG) == dup) {
            duplicates.remove(0);
            if (duplicates.size() > 0) {
              duplicateDownloads.put(uri, duplicates);
            } else {
              duplicateDownloads.remove(uri);
            }
            dup.doDownload();
            return;
          }
        }
      }

      if (!queuedDownLoaderRequests.isEmpty()) {
        LoaderRequest d = queuedDownLoaderRequests.remove(0);
        ApptentiveLog.v("ApptentiveAttachmentLoader starting DL of: " + d.getUrl());
        d.doDownload();
      }
    }
 // called when the download starts
 @Override
 public void onDownloadStart() {
   ApptentiveLog.v("ApptentiveAttachmentLoader onDownloadStarted");
   ImageView imageView = getImageView();
   if (imageView != null) {
     if (loadingTaskCallback != null) {
       loadingTaskCallback.onDownloadStart();
     }
   }
 }
    @Override
    public void onLoadCancelled() {
      ApptentiveLog.v("ApptentiveAttachmentLoader onLoadCancelled: " + uri);
      ImageView imageView = getImageView();
      if (imageView != null && this == imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
        imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);

        if (loadingTaskCallback != null) {
          loadingTaskCallback.onLoadTerminated();
        }
      }
    }
 @Override
 public void loadBitmap(Bitmap b) {
   bitmapMemoryCache.addObjectToCache(
       ImageMemoryCache.generateMemoryCacheEntryKey(uri, imageViewWidth, imageViewHeight), b);
   ImageView imageView = getImageView();
   if (imageView != null && this == imageView.getTag(DRAWABLE_DOWNLOAD_TAG)) {
     ApptentiveLog.v("ApptentiveAttachmentLoader loadDrawable(add to cache)");
     loadDrawable(b);
     imageView.setTag(DRAWABLE_DOWNLOAD_TAG, null);
   }
   mWasDownloaded = false;
 }
    // called when the download is in progress
    @Override
    public void onProgress(int progress) {
      ApptentiveLog.v("ApptentiveAttachmentLoader onProgress: " + progress);
      ImageView imageView = getImageView();
      if (imageView != null) {
        if (loadingTaskCallback != null) {
          loadingTaskCallback.onDownloadProgress(progress);
        }
      }

      ArrayList<LoaderRequest> duplicates = duplicateDownloads.get(uri);
      if (duplicates != null) {
        for (LoaderRequest dup : duplicates) {
          ApptentiveLog.v("ApptentiveAttachmentLoader onProgress (dup): " + progress);
          // update the progress on the duplicate downloads
          if (dup != null
              && dup.getImageView() != null
              && dup.getImageView().getTag(DRAWABLE_DOWNLOAD_TAG) == dup) {
            dup.getLoaderCallback().onDownloadProgress(progress);
          }
        }
      }
    }
    private void cancel() {
      ApptentiveLog.v("ApptentiveAttachmentLoader cancel requested for: " + uri);
      mIsCancelled = true;

      ArrayList<LoaderRequest> duplicates = duplicateDownloads.get(uri);
      if (duplicates != null) {
        duplicates.remove(this);
        if (duplicates.size() > 0) {
          duplicateDownloads.put(uri, duplicates);
        } else {
          duplicateDownloads.remove(uri);
        }
      }

      if (queuedDownLoaderRequests.contains(this)) {
        queuedDownLoaderRequests.remove(this);
      }
      if (mDrawableDownloaderTask != null) {
        mDrawableDownloaderTask.cancel(true);
      }
      if (mDrawableLoaderTask != null) {
        mDrawableLoaderTask.cancel(true);
      }
    }
 @SuppressLint("NewApi")
 private void loadImageFromDisk(ImageView imageView) {
   if (imageView != null && !mIsCancelled) {
     ApptentiveLog.v("ApptentiveAttachmentLoader loadImageFromDisk: " + uri);
     mDrawableLoaderTask = new ApptentiveDrawableLoaderTask(imageView, this);
     try {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
         mDrawableLoaderTask.executeOnExecutor(
             AsyncTask.THREAD_POOL_EXECUTOR,
             uri,
             diskCacheFilePath,
             String.valueOf(imageViewWidth),
             String.valueOf(imageViewHeight));
       } else {
         mDrawableLoaderTask.execute(
             uri,
             diskCacheFilePath,
             String.valueOf(imageViewWidth),
             String.valueOf(imageViewHeight));
       }
     } catch (RejectedExecutionException e) {
     }
   }
 }