@Override
 public void onDownloadError(DownloadableObject downloadableObject) {
   SlideshowPhoto slideshowPhoto = (SlideshowPhoto) downloadableObject;
   Log.w(
       LOG_PREFIX,
       "Unable to download slideshow photo with large photo url "
           + slideshowPhoto.getLargePhoto());
 }
  /**
   * Share the provided photo through other android apps
   *
   * <p>Will share the image as a image/jpg content type and include title and description as extra
   *
   * @param slideshowPhoto
   */
  public void actionSharePhoto(SlideshowPhoto slideshowPhoto) {
    Log.i(LOG_PREFIX, "Attempting to share photo " + slideshowPhoto);
    // TODO: Refactor this code.. rather ugly due to some GoogleTV related hacks

    if (slideshowPhoto != null) {
      Intent shareIntent = new Intent();
      shareIntent.setAction(Intent.ACTION_SEND);
      // we assume the type is image/jpg
      shareIntent.setType("image/jpg");

      String sharedText =
          slideshowPhoto.getTitle()
              + ": "
              + slideshowPhoto.getDescription()
              + "\n\n"
              + getResources().getString(R.string.share_footer);

      // if we have a cached file, add the stream and the sharedText
      // if not, add the url and the sharedText
      if (slideshowPhoto.isCacheExisting(rootFileDirectory)) {
        String path =
            "file://" + rootFileDirectory.getAbsolutePath() + "/" + slideshowPhoto.getFileName();
        Log.i(LOG_PREFIX, "Attempting to pass stream url " + path);
        shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(path));
        shareIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
      } else {
        shareIntent.putExtra(
            Intent.EXTRA_TEXT, slideshowPhoto.getLargePhoto() + "\n\n" + sharedText);
      }

      shareIntent.putExtra(Intent.EXTRA_SUBJECT, slideshowPhoto.getTitle());

      // Start the actual sharing activity
      try {
        List<ResolveInfo> relevantActivities =
            getPackageManager().queryIntentActivities(shareIntent, 0);
        if (AndroidUtils.isGoogleTV(getApplicationContext())
            || relevantActivities == null
            || relevantActivities.size() == 0) {
          Log.i(
              LOG_PREFIX,
              "No activity found that can handle image/jpg. Performing simple text share");
          Intent backupShareIntent = new Intent();
          backupShareIntent.setAction(Intent.ACTION_SEND);
          backupShareIntent.setType("text/plain");
          String backupSharedText = slideshowPhoto.getLargePhoto() + "\n\n" + sharedText;
          backupShareIntent.putExtra(Intent.EXTRA_TEXT, backupSharedText);
          startActivity(backupShareIntent);
        } else {
          startActivity(shareIntent);
        }

      } catch (ActivityNotFoundException e) {
        notifyUser("Unable to share current photo");
      }

    } else {
      notifyUser("Unable to share current photo");
    }
  }
  @Override
  public boolean onMenuItemSelected(int featureId, MenuItem item) {
    switch (item.getItemId()) {
      case R.id.menuSetAs:
        SlideshowPhoto currentPhoto1 = imageAdapter.getItem(gallery.getSelectedItemPosition());
        Analytics.trackPageView(getApplicationContext(), "/setas");
        Analytics.trackEvent(getApplicationContext(), "actions", "setas", currentPhoto1.getTitle());
        actionSetAsWallpaper(currentPhoto1);

        return true;
      case R.id.menuPreferences:
        Analytics.trackPageView(getApplicationContext(), "/preferences");
        Intent iPreferences = new Intent(this, SlideshowPreferences.class);
        startActivity(iPreferences);
        return true;
      case R.id.menuShare:
        SlideshowPhoto currentPhoto2 = imageAdapter.getItem(gallery.getSelectedItemPosition());
        Analytics.trackPageView(getApplicationContext(), "/share");
        Analytics.trackEvent(getApplicationContext(), "actions", "share", currentPhoto2.getTitle());
        actionSharePhoto(currentPhoto2);

        return true;
      case R.id.menuTitle:
        actionToggleTitle();

        return true;
      case R.id.menuPause:
        actionPauseSlideshow();

        return true;
      case R.id.menuPlay:
        actionResumeSlideshow();

        return true;

      default:
        return super.onMenuItemSelected(featureId, item);
    }
  }
  /**
   * Set a photo as the wallpaper (or possibly other apps receiving the intent)
   *
   * @param slideshowPhoto
   */
  public void actionSetAsWallpaper(SlideshowPhoto slideshowPhoto) {
    Uri uri = null;

    // If it is a drawable resource, handle it different
    if (slideshowPhoto instanceof SlideshowPhotoDrawable) {
      Log.i(LOG_PREFIX, "Set as... for one of the first three photos");
      // write the file to a cache dir
      SlideshowPhotoDrawable slideshowPhotoDrawable = (SlideshowPhotoDrawable) slideshowPhoto;
      // didn't work, as crop gets no access to folder
      // File cacheDir = getCacheDir();

      File cacheDir = new File(rootFileDirectory, "temp");
      cacheDir.mkdir();

      int drawableId = slideshowPhotoDrawable.getDrawableId();
      InputStream inputStream = getResources().openRawResource(drawableId);

      File cachedPhoto = FileUtils.writeToFile(cacheDir, "" + drawableId + ".jpg", inputStream);

      if (cachedPhoto == null) {
        notifyUser(getString(R.string.msg_wallpaper_failed_drawable));
        return;
      }

      uri = Uri.fromFile(cachedPhoto);
    } else {
      File filePhoto = new File(rootFileDirectory, slideshowPhoto.getFileName());
      uri = Uri.fromFile(filePhoto);
    }

    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_ATTACH_DATA);
    String mimeType = "image/jpg";

    intent.setDataAndType(uri, mimeType);
    intent.putExtra("mimeType", mimeType);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    Log.i(LOG_PREFIX, "Attempting to set photo as wallpaper uri:" + uri);
    if (AndroidUtils.isGoogleTV(getApplicationContext())) {
      notifyUser(getString(R.string.msg_wallpaper_googletv));
    }

    startActivity(Intent.createChooser(intent, "Set Photo As"));
  }
    public View getView(int position, View convertView, ViewGroup parent) {
      View slideshowView = convertView;
      if (position >= getCount()) {
        return null;
      }

      SlideshowPhoto slideshowPhoto = getItem(position);
      Log.d(LOG_PREFIX, position + " title:" + slideshowPhoto.getTitle());

      boolean copyDrawableFromCachedView = false;
      View cachedView = null;
      Integer mapKey = new Integer(position);
      if (slideshowView == null) {
        // let's check the weak references for this View
        if (mapWeakRefViews.containsKey(mapKey)) {
          // we have a key, but the View may be garbage collected
          WeakReference<View> weakRefView = mapWeakRefViews.get(mapKey);
          cachedView = weakRefView.get();
          if (cachedView == null) {
            // view was cached, but has been deleted.
            // it will be replaced later in this method, so don't bother deleting it from the
            // hashmap
          } else if (cachedView.getParent() != null) {
            // Log.i(LOG_PREFIX,position + " was cached, but had a parent. So close!");
            copyDrawableFromCachedView = true;
          } else {
            Log.d(LOG_PREFIX, position + " returned through weak reference caching. Yeah!");
            return cachedView;
          }
        }

        // if no cached value, create it from the resource definition
        LayoutInflater viewInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        slideshowView = viewInflater.inflate(R.layout.slideshow_item, null);
      }

      ImageView slideshowImageView = (ImageView) slideshowView.findViewById(R.id.slideshow_photo);
      slideshowImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
      slideshowImageView.setAdjustViewBounds(true);
      // make sure we do not exceed the opengl hardware accl max size
      slideshowImageView.setMaxHeight(2048);
      slideshowImageView.setMaxWidth(2048);

      // The preferred Gallery item background
      slideshowImageView.setBackgroundResource(mGalleryItemBackground);
      slideshowImageView.setBackgroundColor(Color.TRANSPARENT);

      slideshowImageView.setTag(LOADING_TAG);
      slideshowImageView.setImageResource(R.drawable.loading);

      // OLD METHOD
      // These lines of code are in a separate async task in order to not block the UI thread for
      // approx 300 ms
      // Drawable drawable = slideshowPhoto.getLargePhotoDrawable(rootPhotoFolder);
      // slideshowImageView.setImageDrawable(drawable);
      // new ReadPhotoFromFileTask(slideshowImageView,slideshowPhoto,rootPhotoFolder).execute();

      // This section applies if we have a cached view, but it cannot be reuse directly since it has
      // a parent
      // let us copy the drawable
      boolean slideShowImageDrawableMissing = true;
      if (copyDrawableFromCachedView == true && cachedView != null) {
        // reusing the drawable from a cached view
        ImageView cachedSlideshowImageView =
            (ImageView) cachedView.findViewById(R.id.slideshow_photo);
        String cachedTag = (String) cachedSlideshowImageView.getTag();
        if (cachedSlideshowImageView != null && cachedTag != null && cachedTag.equals(LOADED_TAG)) {
          slideshowImageView.setImageDrawable(cachedSlideshowImageView.getDrawable());
          slideShowImageDrawableMissing = false;
          Log.d(LOG_PREFIX, position + " Cached photo drawable reused. Yeah!");
        } else {
          Log.i(
              LOG_PREFIX,
              position + " Cached Photo is not loaded yet, so could not use cache. Doh!");
        }
      }

      if (slideShowImageDrawableMissing) {
        // NEW METHOD
        // Add to a last-in/first-out queue
        AsyncQueueableObject queueablePhotoObject = null;
        queueablePhotoObject =
            new QueueablePhotoObject(
                slideshowPhoto,
                slideshowView,
                rootPhotoFolder,
                LOADED_TAG,
                screenWidthPx,
                screenHeightPx);
        asyncReadQueue.add(queueablePhotoObject);
        Log.d(LOG_PREFIX, position + " is being loaded in a background async task");
      }

      TextView slideshowTitle = (TextView) slideshowView.findViewById(R.id.slideshow_title);
      slideshowTitle.setText(slideshowPhoto.getTitle());

      TextView slideshowDescription =
          (TextView) slideshowView.findViewById(R.id.slideshow_description);
      slideshowDescription.setText(slideshowPhoto.getDescription());
      // add scrolling to TextView
      // slideshowDescription.setMovementMethod(new ScrollingMovementMethod());

      // find out if we should hide the text descriptions
      boolean isEmptyTitle = false;
      if (slideshowPhoto.getTitle() == null || "".equals(slideshowPhoto.getTitle().trim())) {
        isEmptyTitle = true;
      }

      if (doDisplayPhotoTitle == false || slideshowPhoto.isPromotion() || isEmptyTitle) {
        slideshowTitle.setVisibility(View.INVISIBLE);
        slideshowDescription.setVisibility(View.INVISIBLE);
        View layout = slideshowView.findViewById(R.id.slideshow_text_background);
        layout.setVisibility(View.INVISIBLE);
      }

      // lastView = slideshowView;
      // lastFileName=slideshowPhoto.getFileName();

      // add the view to our weak reference caching
      WeakReference<View> weakRefView = new WeakReference<View>(slideshowView);
      mapWeakRefViews.put(mapKey, weakRefView);

      // DEBUG
      String classLayoutParam = null;
      Object objectLayoutParam = slideshowView.getLayoutParams();
      if (objectLayoutParam != null) {
        classLayoutParam = objectLayoutParam.getClass().getName();
      }
      Log.d(LOG_PREFIX, "Layout params class=" + classLayoutParam + " value=" + objectLayoutParam);

      return slideshowView;
    }
  /**
   * Called when the list of all photos have been downloaded from backend
   *
   * @param slideShowPhotos Photos in the feed, may not exist in cache yet
   */
  private void actionOnPhotoUrlsDownloaded(List<SlideshowPhoto> slideShowPhotos) {
    Log.i(LOG_PREFIX, "Photo gallery definition downloaded, now looking through the results");

    // Let's add the existing one to the adapter immediately, and send the other to the
    // FileDownloader
    ArrayList<DownloadableObject> notCachedPhotos = new ArrayList<DownloadableObject>(100);
    ArrayList<SlideshowPhoto> cachedPhotos = new ArrayList<SlideshowPhoto>(200);

    for (Iterator<SlideshowPhoto> iterator = slideShowPhotos.iterator(); iterator.hasNext(); ) {
      SlideshowPhoto slideshowPhoto = iterator.next();
      if (slideshowPhoto.isCacheExisting(rootFileDirectory)) {
        cachedPhotos.add(slideshowPhoto);
      } else {
        notCachedPhotos.add(slideshowPhoto);
      }
    }

    if (cachedPhotos.size() > 0) {
      // lets randomize all the cached photos
      long seed = System.nanoTime();
      Collections.shuffle(cachedPhotos, new Random(seed));

      addSlideshowPhoto(cachedPhotos);
    }

    if (notCachedPhotos.size() > 0) {
      // Rules for download
      // 1. Never download on roaming
      if (AndroidUtils.isConnectedRoaming(getApplicationContext())) {
        notifyUser(getString(R.string.msg_connected_roaming));
        return;
      }

      boolean connectOn3G = SlideshowPreferences.doDownloadOn3G(getApplicationContext());
      boolean isConnectedToWifi = AndroidUtils.isConnectedToWifi(getApplicationContext());
      boolean isConnectedToWired = AndroidUtils.isConnectedToWired(getApplicationContext());
      // 2. Do not download if not connected to Wifi and user has not changed connect to Wifi
      // setting
      if (isConnectedToWifi == false && isConnectedToWired == false && connectOn3G == false) {
        if (AndroidUtils.isGoogleTV(getApplicationContext())) {
          String msg =
              "On GoogleTV, but not connected to wifi or wired. Ignoring this. WifiCon="
                  + isConnectedToWifi
                  + " WiredCon="
                  + isConnectedToWired;
          Log.w(LOG_PREFIX, msg);
          isConnectedToWifi = true;
        } else {
          notifyUser(getString(R.string.msg_connected_mobile));
        }
      }

      // 3. Connect if on wifi or if not connected to wifi and wifi setting is changed
      if ((isConnectedToWifi == true || isConnectedToWired == true) || connectOn3G == true) {
        Log.i(
            LOG_PREFIX,
            "Downloading photos. ConnectedToWifi="
                + isConnectedToWifi
                + " ConnectOn3G="
                + connectOn3G);

        // lets randomize all the non-cached photos
        long seed = System.nanoTime();
        Collections.shuffle(notCachedPhotos, new Random(seed));
        fileDownloader =
            new FileDownloader(this.getBaseContext(), this, rootFileDirectory, notCachedPhotos);
        fileDownloader.execute();
      }

    } else {
      Log.i(LOG_PREFIX, "No new photos to download");
    }
  }