Merge "Update hierarchical folders; implement this "the right way"" into jb-ub-mail
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dfae210..6b68507 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -634,7 +634,7 @@
     <!-- Message displayed when trying to play a video that isn't ready [CHAR LIMIT=100] -->
     <string name="photo_view_video_not_ready">Video not available at this time. Please refresh.</string>
 
-        <!-- Message displayed when displaying a place holder image (for photos & videos) [CHAR LIMIT=50] -->
+        <!-- Message displayed when displaying a place holder image (for photos & videos) [CHAR LIMIT=60] -->
     <string name="photo_view_placeholder_image">Item not available at this time. Please refresh.</string>
 
     <!-- Text shown when a photo fails to download. -->
diff --git a/src/com/android/mail/browse/MessageAttachmentTile.java b/src/com/android/mail/browse/MessageAttachmentTile.java
index 613e6f6..7a4b9bc 100644
--- a/src/com/android/mail/browse/MessageAttachmentTile.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -47,7 +47,7 @@
 import com.android.mail.R;
 import com.android.mail.photo.Intents;
 import com.android.mail.photo.Intents.PhotoViewIntentBuilder;
-import com.android.mail.photo.util.MediaStoreUtils;
+import com.android.mail.photo.util.ImageUtils;
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.UIProvider.AttachmentColumns;
 import com.android.mail.providers.UIProvider.AttachmentDestination;
@@ -462,7 +462,7 @@
      * View an attachment by an application on device.
      */
     private void sendViewIntent() {
-        if (MediaStoreUtils.isImageMimeType(Utils.normalizeMimeType(mAttachment.contentType))) {
+        if (ImageUtils.isImageMimeType(Utils.normalizeMimeType(mAttachment.contentType))) {
             final PhotoViewIntentBuilder builder =
                     Intents.newPhotoViewActivityIntentBuilder(getContext());
             builder.setAlbumName(mAttachment.name)
diff --git a/src/com/android/mail/compose/QuotedTextView.java b/src/com/android/mail/compose/QuotedTextView.java
index 69c8296..6fe5c29 100644
--- a/src/com/android/mail/compose/QuotedTextView.java
+++ b/src/com/android/mail/compose/QuotedTextView.java
@@ -207,12 +207,9 @@
     private void respondInline() {
         // Copy the text in the quoted message to the body of the
         // message after stripping the html.
-        HtmlParser parser = new HtmlParser();
-        HtmlDocument doc = parser.parse(getQuotedText().toString());
-        HtmlTreeBuilder builder = new HtmlTreeBuilder();
-        doc.accept(builder);
+        final String plainText = Utils.convertHtmlToPlainText(getQuotedText().toString());
         if (mRespondInlineListener != null) {
-            mRespondInlineListener.onRespondInline("\n" + builder.getTree().getPlainText());
+            mRespondInlineListener.onRespondInline("\n" + plainText);
         }
         // Set quoted text to unchecked and not visible.
         updateCheckedState(false);
diff --git a/src/com/android/mail/photo/BaseFragmentActivity.java b/src/com/android/mail/photo/BaseFragmentActivity.java
index adfd663..e38f4f6 100644
--- a/src/com/android/mail/photo/BaseFragmentActivity.java
+++ b/src/com/android/mail/photo/BaseFragmentActivity.java
@@ -36,11 +36,9 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.widget.ImageButton;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.mail.R;
-import com.android.mail.photo.util.ImageCache;
 
 import java.util.ArrayList;
 
@@ -763,11 +761,4 @@
 
         return null;
     }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        ImageCache.getInstance(this).refresh();
-    }
 }
diff --git a/src/com/android/mail/photo/content/ImageRequest.java b/src/com/android/mail/photo/content/ImageRequest.java
deleted file mode 100644
index 648e769..0000000
--- a/src/com/android/mail/photo/content/ImageRequest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.content;
-
-/**
- * A request for an image.
- */
-public abstract class ImageRequest {
-
-    /**
-     * @return true if this request is known to resolve to a missing image.
-     */
-    public abstract boolean isEmpty();
-}
diff --git a/src/com/android/mail/photo/content/LocalImageRequest.java b/src/com/android/mail/photo/content/LocalImageRequest.java
deleted file mode 100644
index fa30bb2..0000000
--- a/src/com/android/mail/photo/content/LocalImageRequest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.content;
-
-import android.net.Uri;
-
-/**
- * A request for a media image of a specific size.
- */
-public class LocalImageRequest extends ImageRequest {
-    private final Uri mUri;
-    private final int mWidth;
-    private final int mHeight;
-
-    private int mHashCode;
-
-    public LocalImageRequest(int width, int height) {
-        mUri = null;
-        mWidth = width;
-        mHeight = height;
-    }
-
-    /**
-     * @return the original Uri
-     */
-    public Uri getUri() {
-        return mUri;
-    }
-
-    /**
-     * @return the width
-     */
-    public int getWidth() {
-        return mWidth;
-    }
-
-    /**
-     * @return the height
-     */
-    public int getHeight() {
-        return mHeight;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isEmpty() {
-        return mUri == null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        if (mHashCode == 0) {
-            int result = 17;
-            result = 31 * result + mUri.hashCode();
-            result = 31 * result + mWidth;
-            result = 31 * result + mHeight;
-            mHashCode = result;
-        }
-        return mHashCode;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
-
-        if (!(o instanceof LocalImageRequest)) {
-            return false;
-        }
-
-        final LocalImageRequest other = (LocalImageRequest) o;
-        return (mUri.equals(other.mUri) &&
-                mWidth == other.mWidth &&
-                mHeight == other.mHeight);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "LocalImageRequest: " + mUri.toString() + " (" + mWidth + ", " + mHeight + ")";
-    }
-}
diff --git a/src/com/android/mail/photo/content/MediaImageRequest.java b/src/com/android/mail/photo/content/MediaImageRequest.java
deleted file mode 100644
index 612dc7d..0000000
--- a/src/com/android/mail/photo/content/MediaImageRequest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.content;
-
-import android.text.TextUtils;
-
-import com.android.mail.photo.util.ImageUtils;
-
-/**
- * A request for a media image of a specific size.
- */
-public class MediaImageRequest extends ImageRequest {
-
-    private final String mUrl;
-    private final String mMediaType;
-    private final int mWidth;
-    private final int mHeight;
-    private final boolean mCropAndResize;
-
-    private String mDownloadUrl;
-    private int mHashCode;
-
-    public MediaImageRequest() {
-        this(null, null, 0, 0, false);
-    }
-
-    public MediaImageRequest(String url, String mediaType, int size) {
-        this(url, mediaType, size, size, true);
-    }
-
-    public MediaImageRequest(
-            String url, String mediaType, int width, int height, boolean cropAndResize) {
-        if (url == null) {
-            throw new NullPointerException();
-        }
-
-        mUrl = url;
-        mMediaType = mediaType;
-        mWidth = width;
-        mHeight = height;
-        mCropAndResize = cropAndResize;
-    }
-
-    /**
-     * @return the original URL
-     */
-    public String getUrl() {
-        return mUrl;
-    }
-
-    /**
-     * @return the URL
-     */
-    public String getDownloadUrl() {
-        if (mDownloadUrl == null) {
-            if (!mCropAndResize || mWidth == 0) {
-                mDownloadUrl = mUrl;
-            } else if (mWidth == mHeight) {
-                mDownloadUrl = ImageUtils.getCroppedAndResizedUrl(mWidth, mUrl);
-            } else {
-                mDownloadUrl = ImageUtils.getCenterCroppedAndResizedUrl(mWidth, mHeight, mUrl);
-            }
-
-            if (mDownloadUrl.startsWith("//")) {
-                mDownloadUrl = "http:" + mDownloadUrl;
-            }
-        }
-        return mDownloadUrl;
-    }
-
-    /**
-     * @return the media type
-     */
-    public String getMediaType() {
-        return mMediaType;
-    }
-
-    /**
-     * @return the width
-     */
-    public int getWidth() {
-        return mWidth;
-    }
-
-    /**
-     * @return the height
-     */
-    public int getHeight() {
-        return mHeight;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isEmpty() {
-        return mUrl == null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        if (mHashCode == 0) {
-            if (mUrl != null) {
-                mHashCode = mUrl.hashCode();
-            } else {
-                mHashCode = 1;
-            }
-        }
-        return mHashCode;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof MediaImageRequest)) {
-            return false;
-        }
-
-        MediaImageRequest k = (MediaImageRequest) o;
-        return mWidth == k.mWidth && mHeight == k.mHeight
-                && TextUtils.equals(mUrl, k.mUrl)
-                && TextUtils.equals(mMediaType, k.mMediaType);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "MediaImageRequest: " + mMediaType + " " + mUrl + " (" + mWidth
-                + ", " + mHeight + ")";
-    }
-}
diff --git a/src/com/android/mail/photo/util/GifDrawable.java b/src/com/android/mail/photo/graphics/GifDrawable.java
similarity index 99%
rename from src/com/android/mail/photo/util/GifDrawable.java
rename to src/com/android/mail/photo/graphics/GifDrawable.java
index 5c625ea..821794f 100644
--- a/src/com/android/mail/photo/util/GifDrawable.java
+++ b/src/com/android/mail/photo/graphics/GifDrawable.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.android.mail.photo.util;
+package com.android.mail.photo.graphics;
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
diff --git a/src/com/android/mail/photo/loaders/PhotoBitmapLoader.java b/src/com/android/mail/photo/loaders/PhotoBitmapLoader.java
index 49965a5..a1c8743 100644
--- a/src/com/android/mail/photo/loaders/PhotoBitmapLoader.java
+++ b/src/com/android/mail/photo/loaders/PhotoBitmapLoader.java
@@ -30,22 +30,22 @@
  * Loader for the bitmap of a photo.
  */
 public class PhotoBitmapLoader extends AsyncTaskLoader<Bitmap> {
-    private final String mPhotoUrl;
+    private final String mPhotoUri;
 
     private Bitmap mBitmap;
 
-    public PhotoBitmapLoader(Context context, String photoUrl) {
+    public PhotoBitmapLoader(Context context, String photoUri) {
         super(context);
-        mPhotoUrl = photoUrl;
+        mPhotoUri = photoUri;
     }
 
     @Override
     public Bitmap loadInBackground() {
         Context context = getContext();
 
-        if (context != null && mPhotoUrl != null) {
+        if (context != null && mPhotoUri != null) {
             final ContentResolver resolver = context.getContentResolver();
-            return ImageUtils.createLocalBitmap(resolver, Uri.parse(mPhotoUrl),
+            return ImageUtils.createLocalBitmap(resolver, Uri.parse(mPhotoUri),
                     PhotoViewFragment.sPhotoSize);
         }
 
diff --git a/src/com/android/mail/photo/util/FIFEUtil.java b/src/com/android/mail/photo/util/FIFEUtil.java
deleted file mode 100644
index b23182f..0000000
--- a/src/com/android/mail/photo/util/FIFEUtil.java
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.util;
-
-import android.net.Uri;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Useful FIFE image url manipulation routines.
- */
-public class FIFEUtil {
-    private static final Splitter SPLIT_ON_EQUALS = Splitter.on("=").omitEmptyStrings();
-
-    private static final Splitter SPLIT_ON_SLASH = Splitter.on("/").omitEmptyStrings();
-
-    private static final Joiner JOIN_ON_SLASH = Joiner.on("/");
-
-    private static final Pattern FIFE_HOSTED_IMAGE_URL_RE = Pattern.compile("^((http(s)?):)?\\/\\/"
-            + "((((lh[3-6]\\.((ggpht)|(googleusercontent)|(google)))"
-            + "|([1-4]\\.bp\\.blogspot)|(bp[0-3]\\.blogger))\\.com)"
-            + "|(www\\.google\\.com\\/visualsearch\\/lh))\\/");
-
-    private static final String EMPTY_STRING = "";
-
-    // The ImageUrlOptions path part index for legacy Fife image URLs.
-    private static final int LEGACY_URL_PATH_OPTIONS_INDEX = 4;
-
-    // Num of path parts a legacy Fife image base URL contains. A base URL
-    // contains
-    // no ImageUrlOptions nor a filename and is terminated by a slash.
-    private static final int LEGACY_BASE_URL_NUM_PATH_PARTS = 4;
-    // Number of path parts a legacy Fife image URL contains that has both
-    // existing
-    // ImageUrlOptions and a filename.
-    private static final int LEGACY_WITH_OPTIONS_FILENAME = 5;
-
-    // Maximum number of path parts a legacy Fife image URL can contain.
-    private static final int LEGACY_URL_MAX_NUM_PATH_PARTS = 6;
-
-    // Maximum number of path parts a content Fife image URL can contain.
-    private static final int CONTENT_URL_MAX_NUM_PATH_PARTS = 1;
-
-    /**
-     * Add size options to the given url.
-     *
-     * @param size the image size
-     * @param url the url to apply the options to
-     * @param crop if {@code true}, crop the photo to the dimensions
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static String setImageUrlSize(int size, String url, boolean crop) {
-        return setImageUrlSize(size, url, crop, false);
-    }
-
-    /**
-     * Add size options to the given url.
-     *
-     * @param size the image size
-     * @param url the url to apply the options to
-     * @param crop if {@code true}, crop the photo to the dimensions
-     * @param includeMetadata if {@code true}, the image returned by the URL will include meta data
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static String setImageUrlSize(int size, String url, boolean crop,
-            boolean includeMetadata) {
-        if (url == null || !isFifeHostedUrl(url)) {
-            return url;
-        }
-
-        final StringBuffer options = new StringBuffer();
-        options.append("s").append(size);
-        options.append("-d");
-        if (crop) {
-            options.append("-c");
-        }
-        if (includeMetadata) {
-            options.append("-I");
-        }
-
-        final Uri uri = setImageUrlOptions(options.toString(), url);
-        final String returnUrl = makeUriString(uri);
-
-        return returnUrl;
-    }
-
-    /**
-     * Add size options to the given url.
-     *
-     * @param width the width of the image
-     * @param height the height of the image
-     * @param url the url to apply the options to
-     * @param crop if {@code true}, crop the photo to the dimensions
-     * @param includeMetadata if {@code true}, the image returned by the URL will include meta data
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static String setImageUrlSize(int width, int height, String url, boolean crop,
-            boolean includeMetadata) {
-        if (url == null || !isFifeHostedUrl(url)) {
-            return url;
-        }
-
-        final StringBuffer options = new StringBuffer();
-        options.append("w").append(width);
-        options.append("-h").append(height);
-        options.append("-d");
-        if (crop) {
-            options.append("-c");
-        }
-        if (includeMetadata) {
-            options.append("-I");
-        }
-
-        final Uri uri = setImageUrlOptions(options.toString(), url);
-        final String returnUrl = makeUriString(uri);
-
-        return returnUrl;
-    }
-
-    /**
-     * Workaround. When encoding FIFE URL with content image options, the default
-     * implementation for Uri.toString() encodes the equals ['='] as "%3D". The
-     * FIFE servers choke on this and return a 404.
-     */
-    private static String makeUriString(Uri uri) {
-        final StringBuilder builder = new StringBuilder();
-
-        final String scheme = uri.getScheme();
-        if (scheme != null) {
-            builder.append(scheme).append(':');
-        }
-
-        final String encodedAuthority = uri.getEncodedAuthority();
-        if (encodedAuthority != null) {
-            // Even if the authority is "", we still want to append "//".
-            builder.append("//").append(encodedAuthority);
-        }
-
-        final String path = uri.getPath();
-        final String encodedPath = Uri.encode(path, "/=");
-        if (encodedPath != null) {
-            builder.append(encodedPath);
-        }
-
-        final String encodedQuery = uri.getEncodedQuery();
-        if (!TextUtils.isEmpty(encodedQuery)) {
-            builder.append('?').append(encodedQuery);
-        }
-
-        final String encodedFragment = uri.getEncodedFragment();
-        if (!TextUtils.isEmpty(encodedFragment)) {
-            builder.append('#').append(encodedFragment);
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Add image url options to the given url.
-     *
-     * @param options the options to apply
-     * @param url the url to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static Uri setImageUrlOptions(String options, String url) {
-        return setImageUriOptions(options, Uri.parse(url));
-    }
-
-    /**
-     * Add image url options to the given url.
-     *
-     * @param options the options to apply
-     * @param uri the uri to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static Uri setImageUriOptions(String options, Uri uri) {
-        List<String> components = newArrayList(SPLIT_ON_SLASH.split(uri.getPath()));
-
-        // Delegate setting ImageUrlOptions based on the Fife image URL format
-        // determined by the number of path parts the URL contains.
-        int numParts = components.size();
-        if (components.size() > 1 && components.get(0).equals("image")) {
-            --numParts;
-        }
-
-        Uri modifiedUri;
-        if (numParts >= LEGACY_BASE_URL_NUM_PATH_PARTS
-                && numParts <= LEGACY_URL_MAX_NUM_PATH_PARTS) {
-            modifiedUri = setLegacyImageUrlOptions(options, uri);
-        } else if (numParts == CONTENT_URL_MAX_NUM_PATH_PARTS) {
-            modifiedUri = setContentImageUrlOptions(options, uri);
-        } else {
-            // not a valid URI; don't modify anything
-            modifiedUri = uri;
-        }
-        return modifiedUri;
-    }
-
-    /**
-     * Gets image options from the given url.
-     *
-     * @param url the url to get the options for
-     * @return the image options. or {@link #EMPTY_STRING} if options do not exist.
-     */
-    public static String getImageUrlOptions(String url) {
-        return getImageUriOptions(Uri.parse(url));
-    }
-
-    /**
-     * Gets image options from the given uri.
-     *
-     * @param uri the uri to get the options for
-     * @return the image options. or {@link #EMPTY_STRING} if options do not exist.
-     */
-    public static String getImageUriOptions(Uri uri) {
-        List<String> components = newArrayList(SPLIT_ON_SLASH.split(uri.getPath()));
-
-        // Delegate setting ImageUrlOptions based on the Fife image URL format
-        // determined by the number of path parts the URL contains.
-        int numParts = components.size();
-        if (components.size() > 1 && components.get(0).equals("image")) {
-            --numParts;
-        }
-
-        final String options;
-        if (numParts >= LEGACY_BASE_URL_NUM_PATH_PARTS
-                && numParts <= LEGACY_URL_MAX_NUM_PATH_PARTS) {
-            options = getLegacyImageUriOptions(uri);
-        } else if (numParts == CONTENT_URL_MAX_NUM_PATH_PARTS) {
-            options = getContentImageUriOptions(uri);
-        } else {
-            // not a valid URI; don't modify anything
-            options = EMPTY_STRING;
-        }
-        return options;
-    }
-
-    /**
-     * Checks if the host is a valid FIFE host.
-     *
-     * @param url an image url to check
-     * @return {@code true} iff the url has a valid FIFE host
-     */
-    public static boolean isFifeHostedUrl(String url) {
-        if (url == null) {
-            return false;
-        }
-
-        Matcher matcher = FIFE_HOSTED_IMAGE_URL_RE.matcher(url);
-        return matcher.find();
-    }
-
-    /**
-     * Checks if the host is a valid FIFE host.
-     *
-     * @param uri an image url to check
-     * @return {@code true} iff the url has a valid FIFE host
-     */
-    public static boolean isFifeHostedUri(Uri uri) {
-        return isFifeHostedUrl(uri.toString());
-    }
-
-    /**
-     * Add image url options to the given url.
-     *
-     * @param options the options to apply
-     * @param url the url to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    private static Uri setLegacyImageUrlOptions(String options, Uri url) {
-        String path = url.getPath();
-        List<String> components = newArrayList(SPLIT_ON_SLASH.split(path));
-        boolean hasImagePrefix = false;
-
-        if (components.size() > 0 && components.get(0).equals("image")) {
-            components.remove(0);
-            hasImagePrefix = true;
-        }
-
-        int numParts = components.size();
-        boolean isPathSlashTerminated = path.endsWith("/");
-        boolean containsFilenameNoOptions =
-                !isPathSlashTerminated && numParts == LEGACY_WITH_OPTIONS_FILENAME;
-        boolean isBaseUrlFormat = numParts == LEGACY_BASE_URL_NUM_PATH_PARTS;
-
-        // Make room for the options in the path components if no options previously existed.
-        if (containsFilenameNoOptions) {
-            components.add(components.get(LEGACY_URL_PATH_OPTIONS_INDEX));
-        }
-
-        if (isBaseUrlFormat) {
-            components.add(options);
-        } else {
-            components.set(LEGACY_URL_PATH_OPTIONS_INDEX, options);
-        }
-
-        // Put back image component if was there before.
-        if (hasImagePrefix) {
-            components.add(0, "image");
-        }
-
-        // Terminate the new path with a slash if required.
-        if (isPathSlashTerminated) {
-            components.add("");
-        }
-
-        return url.buildUpon().path("/" + JOIN_ON_SLASH.join(components)).build();
-    }
-
-    /**
-     * Add image url options to the given url.
-     *
-     * @param options the options to apply
-     * @param url the url to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    private static Uri setContentImageUrlOptions(String options, Uri url) {
-        List<String> splitPath = newArrayList(SPLIT_ON_EQUALS.split(url.getPath()));
-        String path = splitPath.get(0) + "=" + options;
-
-        return url.buildUpon().path(path).build();
-    }
-
-    /**
-     * Gets image options from the given URI.
-     *
-     * @param uri the URI to get the options for
-     * @return the image options. or {@link #EMPTY_STRING} if options do not exist.
-     */
-    private static String getLegacyImageUriOptions(Uri uri) {
-        String path = uri.getPath();
-        List<String> components = newArrayList(SPLIT_ON_SLASH.split(path));
-
-        if (components.size() > 0 && components.get(0).equals("image")) {
-            components.remove(0);
-        }
-
-        int numParts = components.size();
-        boolean isPathSlashTerminated = path.endsWith("/");
-        boolean containsFilenameNoOptions =
-                !isPathSlashTerminated && numParts == LEGACY_WITH_OPTIONS_FILENAME;
-        boolean isBaseUrlFormat = numParts == LEGACY_BASE_URL_NUM_PATH_PARTS;
-
-        // No options in the URI
-        if (containsFilenameNoOptions) {
-            return EMPTY_STRING;
-        }
-
-        if (!isBaseUrlFormat) {
-            return components.get(LEGACY_URL_PATH_OPTIONS_INDEX);
-        }
-
-        return EMPTY_STRING;
-    }
-
-    /**
-     * Gets image options from the given URI.
-     *
-     * @param uri the URI to get the options for
-     * @return the image options. or {@link #EMPTY_STRING} if options do not exist.
-     */
-    private static String getContentImageUriOptions(Uri uri) {
-        List<String> splitPath = newArrayList(SPLIT_ON_EQUALS.split(uri.getPath()));
-        return (splitPath.size() > 1) ? splitPath.get(1) : EMPTY_STRING;
-    }
-
-    // Private. Just a class full of static functions.
-    private FIFEUtil() {
-    }
-
-
-
-
-    /*
-     * The code below has been shamelessly copied from guava to avoid bringing in it's 700+K
-     * library for just a few lines of code. This is <em>NOT</em> meant to provide a fully
-     * functional replacement. It only provides enough functionality to modify FIFE URLs.
-     */
-
-    /**
-     * Creates a <i>mutable</i> {@code ArrayList} instance containing the given
-     * elements.
-     */
-    private static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
-        // Let ArrayList's sizing logic work, if possible
-        Iterator<? extends E> iterator = elements.iterator();
-        ArrayList<E> list = new ArrayList<E>();
-        while (iterator.hasNext()) {
-            list.add(iterator.next());
-        }
-        return list;
-    }
-
-    /**
-     * Joins pieces of text with a separator.
-     */
-    private static class Joiner {
-        public static Joiner on(String separator) {
-            return new Joiner(separator);
-        }
-
-        private final String separator;
-
-        private Joiner(String separator) {
-            this.separator = separator;
-        }
-
-        /**
-         * Appends each of part, using the configured separator between each.
-         */
-        public final StringBuilder appendTo(StringBuilder builder, Iterable<?> parts) {
-            Iterator<?> iterator = parts.iterator();
-            if (iterator.hasNext()) {
-                builder.append(toString(iterator.next()));
-                while (iterator.hasNext()) {
-                    builder.append(separator);
-                    builder.append(toString(iterator.next()));
-                }
-            }
-            return builder;
-        }
-
-        /**
-         * Returns a string containing the string representation of each of
-         * {@code parts}, using the previously configured separator between
-         * each.
-         */
-        public final String join(Iterable<?> parts) {
-            return appendTo(new StringBuilder(), parts).toString();
-        }
-
-        CharSequence toString(Object part) {
-            return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
-        }
-    }
-
-    /**
-     * Divides strings into substrings, by recognizing a separator (a.k.a. "delimiter").
-     */
-    static class Splitter {
-        private final boolean omitEmptyStrings;
-        private final Strategy strategy;
-
-        private Splitter(Strategy strategy) {
-            this(strategy, false);
-        }
-
-        private Splitter(Strategy strategy, boolean omitEmptyStrings) {
-            this.strategy = strategy;
-            this.omitEmptyStrings = omitEmptyStrings;
-        }
-
-        public static Splitter on(final String separator) {
-            if (separator == null || separator.length() == 0) {
-                throw new IllegalArgumentException("separator may not be empty or null");
-            }
-
-            return new Splitter(new Strategy() {
-                @Override
-                public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
-                    return new SplittingIterator(splitter, toSplit) {
-                        @Override
-                        public int separatorStart(int start) {
-                            int delimeterLength = separator.length();
-
-                            positions: for (
-                                    int p = start, last = toSplit.length() - delimeterLength;
-                                    p <= last;
-                                    p++) {
-                                for (int i = 0; i < delimeterLength; i++) {
-                                    if (toSplit.charAt(i + p) != separator.charAt(i)) {
-                                        continue positions;
-                                    }
-                                }
-                                return p;
-                            }
-                            return -1;
-                        }
-
-                        @Override
-                        public int separatorEnd(int separatorPosition) {
-                            return separatorPosition + separator.length();
-                        }
-                    };
-                }
-            });
-        }
-
-        public Splitter omitEmptyStrings() {
-            return new Splitter(strategy, true);
-        }
-
-        public Iterable<String> split(final CharSequence sequence) {
-            return new Iterable<String>() {
-                @Override
-                public Iterator<String> iterator() {
-                    return strategy.iterator(Splitter.this, sequence);
-                }
-            };
-        }
-
-        private interface Strategy {
-            Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
-        }
-
-        private abstract static class SplittingIterator extends AbstractIterator<String> {
-            final CharSequence toSplit;
-            final boolean omitEmptyStrings;
-
-            abstract int separatorStart(int start);
-
-            abstract int separatorEnd(int separatorPosition);
-
-            int offset = 0;
-
-            protected SplittingIterator(Splitter splitter, CharSequence toSplit) {
-                this.omitEmptyStrings = splitter.omitEmptyStrings;
-                this.toSplit = toSplit;
-            }
-
-            @Override
-            protected String computeNext() {
-                while (offset != -1) {
-                    int start = offset;
-                    int end;
-
-                    int separatorPosition = separatorStart(offset);
-                    if (separatorPosition == -1) {
-                        end = toSplit.length();
-                        offset = -1;
-                    } else {
-                        end = separatorPosition;
-                        offset = separatorEnd(separatorPosition);
-                    }
-
-                    if (omitEmptyStrings && start == end) {
-                        continue;
-                    }
-
-                    return toSplit.subSequence(start, end).toString();
-                }
-                return endOfData();
-            }
-        }
-
-        private static abstract class AbstractIterator<T> implements Iterator<T> {
-            State state = State.NOT_READY;
-
-            enum State {
-                READY, NOT_READY, DONE, FAILED,
-            }
-
-            T next;
-
-            protected abstract T computeNext();
-
-            protected final T endOfData() {
-                state = State.DONE;
-                return null;
-            }
-
-            @Override
-            public final boolean hasNext() {
-                if (state == State.FAILED) {
-                    throw new IllegalStateException();
-                }
-
-                switch (state) {
-                    case DONE:
-                        return false;
-                    case READY:
-                        return true;
-                    default:
-                }
-                return tryToComputeNext();
-            }
-
-            boolean tryToComputeNext() {
-                state = State.FAILED; // temporary pessimism
-                next = computeNext();
-                if (state != State.DONE) {
-                    state = State.READY;
-                    return true;
-                }
-                return false;
-            }
-
-            @Override
-            public final T next() {
-                if (!hasNext()) {
-                    throw new NoSuchElementException();
-                }
-                state = State.NOT_READY;
-                return next;
-            }
-
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        }
-    }
-}
diff --git a/src/com/android/mail/photo/util/ImageCache.java b/src/com/android/mail/photo/util/ImageCache.java
deleted file mode 100644
index 31194c4..0000000
--- a/src/com/android/mail/photo/util/ImageCache.java
+++ /dev/null
@@ -1,1274 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.util;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Looper;
-import android.os.Message;
-import android.support.v4.util.LruCache;
-
-import com.android.mail.R;
-import com.android.mail.photo.content.ImageRequest;
-import com.android.mail.photo.content.LocalImageRequest;
-import com.android.mail.photo.content.MediaImageRequest;
-
-import java.lang.ref.SoftReference;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Asynchronously loads images and maintains a cache of those.
- */
-public class ImageCache implements Callback {
-
-    /**
-     * The listener interface notifying of avatar changes.
-     */
-    public interface OnAvatarChangeListener {
-
-        /**
-         * Invoked if a new avatar has been loaded for the specified Gaia ID.
-         */
-        void onAvatarChanged(String gaiaId);
-    }
-
-    /**
-     * The listener interface notifying of media image changes.
-     */
-    public interface OnMediaImageChangeListener {
-
-        /**
-         * Invoked if a new media image has been loaded for the specified URL.
-         */
-        void onMediaImageChanged(String url);
-    }
-
-    /**
-     * The listener interface notifying of image changes.
-     */
-    public interface OnRemoteImageChangeListener {
-
-        /**
-         * Invoked if a new media image has been loaded for the specified URL.
-         */
-        void onRemoteImageChanged(ImageRequest request, Bitmap bitmap);
-    }
-
-    /**
-     * The listener interface notifying of remote image changes.
-     */
-    public interface OnRemoteDrawableChangeListener extends OnRemoteImageChangeListener {
-
-        /**
-         * Invoked if a new image has been loaded for the specified URL.
-         */
-        void onRemoteImageChanged(ImageRequest request, Drawable drawable);
-    }
-
-    /**
-     * The listener interface notifying a listener that an image load request has been completed.
-     */
-    public interface OnImageRequestCompleteListener {
-
-        /**
-         * Invoked when a request is complete.
-         */
-        void onImageRequestComplete(ImageRequest request);
-    }
-
-    /**
-     * The callback interface that must be implemented by the views requesting images.
-     */
-    public interface ImageConsumer {
-
-        /**
-         * @param bitmap The bitmap
-         * @param loading The flag indicating if the image is still loading (if
-         *            true, bitmap will be null).
-         */
-        void setBitmap(Bitmap bitmap, boolean loading);
-    }
-
-    /**
-     * The callback interface that can optionally be implemented by the views
-     * requesting images if they want to support animated drawables.
-     */
-    public interface DrawableConsumer extends ImageConsumer {
-
-        /**
-         * @param drawable The image
-         * @param loading The flag indicating if the image is still loading (if
-         *            true, bitmap will be null).
-         */
-        void setDrawable(Drawable drawable, boolean loading);
-    }
-
-    // Logging.
-    static final String TAG = "ImageCache";
-
-    private static final String LOADER_THREAD_NAME = "ImageCache";
-
-    /**
-     * Type of message sent by the UI thread to itself to indicate that some photos
-     * need to be loaded.
-     */
-    private static final int MESSAGE_REQUEST_LOADING = 1;
-
-    /**
-     * Type of message sent by the loader thread to indicate that some photos have
-     * been loaded.
-     */
-    private static final int MESSAGE_IMAGES_LOADED = 2;
-
-    /**
-     * Type of message sent to indicate that an avatar has changed.
-     */
-    private static final int MESSAGE_AVATAR_CHANGED = 3;
-
-    /**
-     * Type of message sent to indicate that a media image has changed.
-     */
-    private static final int MESSAGE_MEDIA_IMAGE_CHANGED = 4;
-
-    /**
-     * Type of message sent to indicate that an image has changed.
-     */
-    private static final int MESSAGE_REMOTE_IMAGE_CHANGED = 5;
-
-    private static final byte[] EMPTY_ARRAY = new byte[0];
-
-    /**
-     * Maintains the state of a particular photo.
-     */
-    private static class ImageHolder {
-        final byte[] bytes;
-        final boolean complete;
-
-        volatile boolean fresh;
-
-        /**
-         * Either {@link Bitmap} or {@link Drawable}.
-         */
-        Object image;
-        SoftReference<Object> imageRef;
-
-
-        public ImageHolder(byte[] bytes, boolean complete) {
-            this.bytes = bytes;
-            this.fresh = true;
-            this.complete = complete;
-        }
-    }
-
-    private static class MediaImageChangeNotification {
-        MediaImageRequest request;
-        byte[] imageBytes;
-    }
-
-    private static class RemoteImageChangeNotification {
-        ImageRequest request;
-        byte[] imageBytes;
-    }
-
-    private static final float ESTIMATED_BYTES_PER_PIXEL = 0.3f;
-
-    private static int sTinyAvatarEstimatedSize;
-    private static int sSmallAvatarEstimatedSize;
-    private static int sMediumAvatarEstimatedSize;
-
-    private static boolean sUseSoftReferences;
-
-    private final Context mContext;
-
-    private static HashSet<OnAvatarChangeListener> mAvatarListeners =
-            new HashSet<OnAvatarChangeListener>();
-
-    private static HashSet<OnMediaImageChangeListener> mMediaImageListeners =
-            new HashSet<OnMediaImageChangeListener>();
-
-    private static HashSet<OnRemoteImageChangeListener> mRemoteImageListeners =
-            new HashSet<OnRemoteImageChangeListener>();
-
-    private static HashSet<OnImageRequestCompleteListener> mRequestCompleteListeners =
-            new HashSet<OnImageRequestCompleteListener>();
-
-    /**
-     * An LRU cache for image holders. The cache contains bytes for images just
-     * as they come from the database. Each holder has a soft reference to the
-     * actual image.
-     */
-    private final LruCache<ImageRequest, ImageHolder> mImageHolderCache;
-
-    /**
-     * Cache size threshold at which images will not be preloaded.
-     */
-    private final int mImageHolderCacheRedZoneBytes;
-
-    /**
-     * Level 2 LRU cache for images. This is a smaller cache that holds
-     * the most recently used images to save time on decoding
-     * them from bytes (the bytes are stored in {@link #mImageHolderCache}.
-     */
-    private final LruCache<ImageRequest, Object> mImageCache;
-
-    /**
-     * A map from {@link ImageConsumer} to the corresponding {@link ImageRequest}. Please
-     * note that this request may change before the photo loading request is
-     * started.
-     */
-    private final ConcurrentHashMap<ImageConsumer, ImageRequest> mPendingRequests =
-            new ConcurrentHashMap<ImageConsumer, ImageRequest>();
-
-    /**
-     * Handler for messages sent to the UI thread.
-     */
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), this);
-
-//    /**
-//     * Thread responsible for loading photos from the database. Created upon
-//     * the first request.
-//     */
-//    private LoaderThread mLoaderThread;
-
-    /**
-     * A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time.
-     */
-    private boolean mLoadingRequested;
-
-    /**
-     * Flag indicating if the image loading is paused.
-     */
-    private boolean mPaused;
-
-    private static ImageCache sInstance;
-
-    public static synchronized ImageCache getInstance(Context context) {
-
-        // We can use one global instance provided that we bind to the
-        // application context instead of the context that is passed in.
-        // Otherwise this static instance would retain the supplied context and
-        // cause a leak.
-        if (sInstance == null) {
-            sInstance = new ImageCache(context.getApplicationContext());
-        }
-        return sInstance;
-    }
-
-    private ImageCache(Context context) {
-        mContext = context;
-
-        Resources resources = context.getApplicationContext().getResources();
-        mImageCache = new LruCache<ImageRequest, Object>(
-                resources.getInteger(R.integer.config_image_cache_max_bitmaps));
-        int maxBytes = resources.getInteger(R.integer.config_image_cache_max_bytes);
-        mImageHolderCache = new LruCache<ImageRequest, ImageHolder>(maxBytes) {
-
-            /**
-             * {@inheritDoc}
-             */
-            @Override
-            protected int sizeOf(ImageRequest request, ImageHolder value) {
-                return value.bytes != null ? value.bytes.length : 0;
-            }
-        };
-
-        mImageHolderCacheRedZoneBytes = (int) (maxBytes * 0.9);
-
-        if (sTinyAvatarEstimatedSize == 0) {
-//            sTinyAvatarEstimatedSize = (int) (ESTIMATED_BYTES_PER_PIXEL
-//                    * EsAvatarData.getTinyAvatarSize(context)
-//                    * EsAvatarData.getTinyAvatarSize(context));
-//            sSmallAvatarEstimatedSize = (int) (ESTIMATED_BYTES_PER_PIXEL
-//                    * EsAvatarData.getSmallAvatarSize(context)
-//                    * EsAvatarData.getSmallAvatarSize(context));
-//            sMediumAvatarEstimatedSize = (int) (ESTIMATED_BYTES_PER_PIXEL
-//                    * EsAvatarData.getMediumAvatarSize(context)
-//                    * EsAvatarData.getMediumAvatarSize(context));
-
-            sUseSoftReferences = Build.VERSION.SDK_INT >= 11;
-        }
-    }
-
-//    /**
-//     * Returns an estimate of the avatar size in bytes.
-//     */
-//    private int getEstimatedSizeInBytes(AvatarRequest request) {
-//        switch (request.getSize()) {
-//            case AvatarRequest.TINY: return sTinyAvatarEstimatedSize;
-//            case AvatarRequest.SMALL: return sSmallAvatarEstimatedSize;
-//            case AvatarRequest.MEDIUM: return sMediumAvatarEstimatedSize;
-//        }
-//        return 0;
-//    }
-
-    /**
-     * Clears cache.
-     */
-    public void clear() {
-        mImageHolderCache.evictAll();
-        mImageCache.evictAll();
-        mPendingRequests.clear();
-    }
-
-//    /**
-//     * Starts preloading photos in the background.
-//     */
-//    public void preloadAvatarsInBackground(List<AvatarRequest> requests) {
-//        ensureLoaderThread();
-//
-//        boolean preloadingNeeded = touchRequestedEntries(requests);
-//        int totalTinyAvatarSize = touchTinyAvatars();
-//
-//        if (!preloadingNeeded) {
-//            return;
-//        }
-//
-//        requests = trimCache(requests, totalTinyAvatarSize);
-//
-//        mLoaderThread.startPreloading(requests);
-//    }
-
-//    /**
-//     * Adjust the LRU order of the requested images that are already cached to prevent them
-//     * from being evicted by preloading.
-//     */
-//    private boolean touchRequestedEntries(List<AvatarRequest> requests) {
-//        boolean cacheMissed = false;
-//        for (int i = requests.size() - 1; i >= 0; i--) {
-//            AvatarRequest request = requests.get(i);
-//            ImageHolder holder = mImageHolderCache.get(request);
-//            if (holder != null) {
-//                mImageHolderCache.put(request, holder);
-//            } else {
-//                cacheMissed = true;
-//            }
-//        }
-//
-//        return cacheMissed;
-//    }
-
-//    /**
-//     * Moves tiny avatars to the top of the LRU order to try and keep them from being evicted.
-//     * Caching tiny avatars is highly cost-effective.
-//     *
-//     * @return The total size of all tiny avatars in cache.
-//     */
-//    private int touchTinyAvatars() {
-//        int totalSize = 0;
-//
-//        Iterator<Entry<ImageRequest, ImageHolder>> iterator =
-//                mImageHolderCache.snapshot().entrySet().iterator();
-//        while (iterator.hasNext()) {
-//            Entry<ImageRequest, ImageHolder> entry = iterator.next();
-//            ImageRequest request = entry.getKey();
-//            if ((request instanceof AvatarRequest)
-//                    && ((AvatarRequest) request).getSize() == AvatarRequest.TINY) {
-//                ImageHolder holder = entry.getValue();
-//                if (holder.bytes != null) {
-//                    totalSize += holder.bytes.length;
-//                }
-//
-//                mImageHolderCache.put(request, holder);
-//            }
-//        }
-//
-//        return totalSize;
-//    }
-
-//    /**
-//     * Reduces the size of cache before preloading.
-//     */
-//    private List<AvatarRequest> trimCache(List<AvatarRequest> requests, int totalTinyAvatarSize) {
-//        int preferredCacheSize = mImageHolderCacheRedZoneBytes;
-//        int estimatedMemoryUse = totalTinyAvatarSize;
-//        for (int i = 0; i < requests.size(); i++) {
-//            if (estimatedMemoryUse >= mImageHolderCacheRedZoneBytes) {
-//                trimCache(preferredCacheSize);
-//                return requests.subList(0, i);
-//            }
-//
-//            AvatarRequest request = requests.get(i);
-//            ImageHolder holder = mImageHolderCache.get(request);
-//            if (holder != null && holder.bytes != null) {
-//                estimatedMemoryUse += holder.bytes.length;
-//            } else {
-//                int bytes = getEstimatedSizeInBytes(request);
-//                preferredCacheSize -= bytes;
-//                estimatedMemoryUse += bytes;
-//            }
-//        }
-//
-//        trimCache(preferredCacheSize);
-//        return requests;
-//    }
-
-    /**
-     * Shrinks cache to the desired size.
-     */
-    private void trimCache(int size) {
-        Iterator<Entry<ImageRequest, ImageHolder>> iterator =
-                mImageHolderCache.snapshot().entrySet().iterator();
-        while (mImageHolderCache.size() > size && iterator.hasNext()) {
-            mImageHolderCache.remove(iterator.next().getKey());
-        }
-    }
-
-    /**
-     * Evicts avatars that were requested but never loaded.  This will force them to be
-     * requested again if needed.
-     */
-    public void refresh() {
-        Iterator<ImageHolder> iterator = mImageHolderCache.snapshot().values().iterator();
-        while (iterator.hasNext()) {
-            ImageHolder holder = iterator.next();
-            if (!holder.complete) {
-                holder.fresh = false;
-            }
-        }
-    }
-
-    /**
-     * Requests asynchronous photo loading for the specified request.
-     *
-     * @param consumer Image consumer
-     * @param request The combination of URL, type and size.
-     */
-    public void loadImage(ImageConsumer consumer, ImageRequest request) {
-        loadImage(consumer, request, true);
-    }
-
-    /**
-     * Requests an asynchronous refresh of the image for the specified request.
-     *
-     * @param consumer Image consumer
-     * @param request The combination of URL, type and size.
-     */
-    public void refreshImage(ImageConsumer consumer, ImageRequest request) {
-        loadImage(consumer, request, false);
-    }
-
-    /**
-     * Evicts all local images from the cache.
-     */
-    public void evictAllLocalImages() {
-        Set<ImageRequest> iterator = mImageHolderCache.snapshot().keySet();
-        for (ImageRequest request : iterator) {
-            if (request instanceof LocalImageRequest) {
-                mImageCache.remove(request);
-                mImageHolderCache.remove(request);
-            }
-        }
-    }
-
-    private void loadImage(ImageConsumer consumer, ImageRequest request,
-            boolean clearIfNotCached) {
-        if (request.isEmpty()) {
-            // No photo is needed
-            consumer.setBitmap(null, false);
-            notifyRequestComplete(request);
-            mPendingRequests.remove(consumer);
-        } else {
-            boolean loaded = loadCachedImage(consumer, request, clearIfNotCached);
-            if (loaded) {
-                mPendingRequests.remove(consumer);
-            } else {
-                mPendingRequests.put(consumer, request);
-                if (!mPaused) {
-                    // Send a request to start loading photos
-                    requestLoading();
-                }
-            }
-        }
-    }
-
-    /**
-     * Registers an avatar change listener.
-     */
-    public void registerAvatarChangeListener(OnAvatarChangeListener listener) {
-        mAvatarListeners.add(listener);
-    }
-
-    /**
-     * Unregisters an avatar change listener.
-     */
-    public void unregisterAvatarChangeListener(OnAvatarChangeListener listener) {
-        mAvatarListeners.remove(listener);
-    }
-
-//    /**
-//     * Sends a notification to all registered listeners that the avatar for the
-//     * specified Gaia ID has changed.
-//     */
-//    public void notifyAvatarChange(String gaiaId) {
-//        if (gaiaId == null) {
-//            return;
-//        }
-//
-//        ensureLoaderThread();
-//        mLoaderThread.notifyAvatarChange(gaiaId);
-//    }
-
-    /**
-     * Registers a media image change listener.
-     */
-    public void registerMediaImageChangeListener(OnMediaImageChangeListener listener) {
-        mMediaImageListeners.add(listener);
-    }
-
-    /**
-     * Unregisters a media image change listener.
-     */
-    public void unregisterMediaImageChangeListener(OnMediaImageChangeListener listener) {
-        mMediaImageListeners.remove(listener);
-    }
-
-//    /**
-//     * Sends a notification to all registered listeners that the media image for the
-//     * specified URL has changed.
-//     */
-//    public void notifyMediaImageChange(MediaImageRequest request, byte[] imageBytes) {
-//        ensureLoaderThread();
-//        MediaImageChangeNotification notification = new MediaImageChangeNotification();
-//        notification.request = request;
-//        notification.imageBytes = imageBytes;
-//        mLoaderThread.notifyMediaImageChange(notification);
-//    }
-
-    /**
-     * Registers a remote image change listener.
-     */
-    public void registerRemoteImageChangeListener(OnRemoteImageChangeListener listener) {
-        mRemoteImageListeners.add(listener);
-    }
-
-    /**
-     * Unregisters a remote image change listener.
-     */
-    public void unregisterRemoteImageChangeListener(OnRemoteImageChangeListener listener) {
-        mRemoteImageListeners.remove(listener);
-    }
-
-//    /**
-//     * Sends a notification to all registered listeners that the remote image for the
-//     * specified URL has changed.
-//     */
-//    public void notifyRemoteImageChange(ImageRequest request, byte[] imageBytes) {
-//        ensureLoaderThread();
-//        RemoteImageChangeNotification notification = new RemoteImageChangeNotification();
-//        notification.request = request;
-//        notification.imageBytes = imageBytes;
-//        mLoaderThread.notifyRemoteImageChange(notification);
-//    }
-
-    /**
-     * Registers an image request completion listener.
-     */
-    public void registerRequestCompleteListener(OnImageRequestCompleteListener listener) {
-        mRequestCompleteListeners.add(listener);
-    }
-
-    /**
-     * Unregisters an image request completion listener.
-     */
-    public void unregisterRequestCompleteListener(OnImageRequestCompleteListener listener) {
-        mRequestCompleteListeners.remove(listener);
-    }
-
-    private void notifyRequestComplete(ImageRequest request) {
-        for (OnImageRequestCompleteListener listener : mRequestCompleteListeners) {
-            listener.onImageRequestComplete(request);
-        }
-    }
-
-    /**
-     * Checks if the photo is present in cache.  If so, sets the photo on the view.
-     *
-     * @return false if the photo needs to be (re)loaded from the provider.
-     */
-    private boolean loadCachedImage(ImageConsumer consumer, ImageRequest request,
-            boolean clearIfNotCached) {
-        ImageHolder holder = mImageHolderCache.get(request);
-        if (holder == null) {
-            if (clearIfNotCached) {
-                // The bitmap has not been loaded - should display the placeholder image.
-                consumer.setBitmap(null, true);
-            }
-            return false;
-        }
-
-        // Put this holder on top of the LRU list
-        mImageHolderCache.put(request, holder);
-
-        if (holder.bytes == null) {
-            if (holder.complete) {
-                consumer.setBitmap(null, false);
-                notifyRequestComplete(request);
-            } else {
-                // The bitmap has not been loaded from server - should display a placeholder.
-                consumer.setBitmap(null, true);
-            }
-            return holder.fresh;
-        }
-
-        // Optionally decode bytes into a bitmap.
-        inflateImage(request, holder);
-
-        Object image = holder.image;
-        if (image instanceof Bitmap) {
-            consumer.setBitmap((Bitmap) image, false);
-        } else if (consumer instanceof DrawableConsumer) {
-            ((DrawableConsumer)consumer).setDrawable((Drawable) image, false);
-        } else if (image instanceof GifDrawable) {
-            consumer.setBitmap(((GifDrawable)image).getFirstFrame(), false);
-        } else if (image != null) {
-            throw new UnsupportedOperationException("Cannot handle drawables of type "
-                    + image.getClass());
-        }
-
-        notifyRequestComplete(request);
-
-        // Put the bitmap in the LRU cache
-        if (image != null && holder.fresh) {
-            mImageCache.put(request, image);
-        }
-
-        // Soften the reference
-        holder.image = null;
-
-        return holder.fresh;
-    }
-
-//    /**
-//     * Returns a photo from cache or null if it is not cached.  Does not trigger a load.
-//     * Returns an empty byte array if the photo is known to be missing.
-//     */
-//    public byte[] getCachedAvatar(AvatarRequest request) {
-//        ImageHolder holder = mImageHolderCache.get(request);
-//        if (holder == null || !holder.fresh) {
-//            return null;
-//        }
-//
-//        if (holder.bytes == null) {
-//            return EMPTY_ARRAY;
-//        }
-//
-//        return holder.bytes;
-//    }
-
-    /**
-     * If necessary, decodes bytes stored in the holder to Bitmap.  As long as the
-     * bitmap is held either by {@link #mImageCache} or by a soft reference in
-     * the holder, it will not be necessary to decode the bitmap.
-     */
-    private void inflateImage(ImageRequest request, ImageHolder holder) {
-        if (holder.image != null) {
-            return;
-        }
-
-        byte[] bytes = holder.bytes;
-        if (bytes == null || bytes.length == 0) {
-            return;
-        }
-
-        holder.image = mImageCache.get(request);
-        if (holder.image != null) {
-            return;
-        }
-
-        // Check the soft reference.  If will be retained if the bitmap is also
-        // in the LRU cache, so we don't need to check the LRU cache explicitly.
-        if (holder.imageRef != null) {
-            holder.image = holder.imageRef.get();
-            if (holder.image != null) {
-                return;
-            }
-        }
-
-        try {
-            holder.image = ImageUtils.decodeMedia(bytes);
-            if (holder.image == null) {
-                holder.imageRef = null;
-            } else if (sUseSoftReferences) {
-                holder.imageRef = new SoftReference<Object>(holder.image);
-            }
-        } catch (OutOfMemoryError e) {
-            // Do nothing - the photo will appear to be missing
-        }
-    }
-
-    public void pause() {
-        mPaused = true;
-    }
-
-    public void resume() {
-        mPaused = false;
-        if (!mPendingRequests.isEmpty()) {
-            requestLoading();
-        }
-    }
-
-    /**
-     * Sends a message to this thread itself to start loading images.  If the current
-     * view contains multiple image views, all of those image views will get a chance
-     * to request their respective photos before any of those requests are executed.
-     * This allows us to load images in bulk.
-     */
-    private void requestLoading() {
-        if (!mLoadingRequested) {
-            mLoadingRequested = true;
-            mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
-        }
-    }
-
-    /**
-     * Processes requests on the main thread.
-     */
-    @Override
-    public boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MESSAGE_REQUEST_LOADING: {
-                mLoadingRequested = false;
-                if (!mPaused) {
-//                    ensureLoaderThread();
-//                    mLoaderThread.requestLoading();
-                }
-                return true;
-            }
-
-            case MESSAGE_IMAGES_LOADED: {
-                if (!mPaused) {
-                    processLoadedImages();
-                }
-                return true;
-            }
-
-//            case MESSAGE_AVATAR_CHANGED: {
-//                String gaiaId = (String) msg.obj;
-//
-//                evictImage(new AvatarRequest(gaiaId, AvatarRequest.TINY));
-//                evictImage(new AvatarRequest(gaiaId, AvatarRequest.SMALL));
-//                evictImage(new AvatarRequest(gaiaId, AvatarRequest.MEDIUM));
-//
-//                for (OnAvatarChangeListener listener : mAvatarListeners) {
-//                    listener.onAvatarChanged(gaiaId);
-//                }
-//                return true;
-//            }
-
-            case MESSAGE_MEDIA_IMAGE_CHANGED: {
-                MediaImageChangeNotification notification = (MediaImageChangeNotification) msg.obj;
-                String url = notification.request.getUrl();
-                for (ImageRequest request : mImageHolderCache.snapshot().keySet()) {
-                    if (!request.equals(notification.request)
-                        && (request instanceof MediaImageRequest)
-                        && url.equals(((MediaImageRequest) request).getUrl())) {
-                        evictImage(request);
-                    }
-                }
-
-                for (OnMediaImageChangeListener listener : mMediaImageListeners) {
-                    listener.onMediaImageChanged(url);
-                }
-                return true;
-            }
-
-            case MESSAGE_REMOTE_IMAGE_CHANGED: {
-                final RemoteImageChangeNotification notification =
-                        (RemoteImageChangeNotification) msg.obj;
-                final ImageRequest notificationRequest = notification.request;
-                final ImageHolder holder = mImageHolderCache.get(notificationRequest);
-                final Object image = (holder != null) ? holder.image : null;
-
-                for (OnRemoteImageChangeListener listener : mRemoteImageListeners) {
-                    if (image instanceof Bitmap || image == null) {
-                        listener.onRemoteImageChanged(notificationRequest, (Bitmap) image);
-                    } else if (image instanceof GifDrawable) {
-                        GifDrawable drawable = (GifDrawable) image;
-                        if (listener instanceof OnRemoteDrawableChangeListener) {
-                            ((OnRemoteDrawableChangeListener) listener).onRemoteImageChanged(
-                                    notificationRequest, drawable);
-                        } else {
-                            listener.onRemoteImageChanged(
-                                    notificationRequest, drawable.getFirstFrame());
-                        }
-                    } else {
-                        throw new UnsupportedOperationException("Unsupported remote image type "
-                                + image.getClass());
-                    }
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void evictImage(ImageRequest request) {
-        mImageCache.remove(request);
-        ImageHolder holder = mImageHolderCache.get(request);
-        if (holder != null) {
-            holder.fresh = false;
-        }
-    }
-
-//    public void ensureLoaderThread() {
-//        if (mLoaderThread == null) {
-//            mLoaderThread = new LoaderThread(mContext.getContentResolver());
-//            mLoaderThread.start();
-//        }
-//    }
-
-    /**
-     * Goes over pending loading requests and displays loaded photos.  If some of the
-     * photos still haven't been loaded, sends another request for image loading.
-     */
-    private void processLoadedImages() {
-        Iterator<ImageConsumer> iterator = mPendingRequests.keySet().iterator();
-        while (iterator.hasNext()) {
-            ImageConsumer consumer = iterator.next();
-            ImageRequest request = mPendingRequests.get(consumer);
-            boolean loaded = loadCachedImage(consumer, request, false);
-            if (loaded) {
-                iterator.remove();
-            }
-        }
-
-        softenCache();
-
-        if (!mPendingRequests.isEmpty()) {
-            requestLoading();
-        }
-    }
-
-    /**
-     * Removes strong references to loaded images to allow them to be garbage collected
-     * if needed.  Some of the images will still be retained by {@link #mImageCache}.
-     */
-    private void softenCache() {
-        for (ImageHolder holder : mImageHolderCache.snapshot().values()) {
-            holder.image = null;
-        }
-    }
-
-    /**
-     * Stores the supplied image in cache.
-     */
-    private void deliverImage(ImageRequest request, byte[] bytes, boolean available,
-            boolean preloading) {
-        ImageHolder holder = new ImageHolder(bytes, available);
-        holder.fresh = true;
-
-        // Unless this image is being preloaded, decode it right away while
-        // we are still on the background thread.
-        if (available && !preloading) {
-            inflateImage(request, holder);
-        }
-
-        mImageHolderCache.put(request, holder);
-    }
-
-    /**
-     * Populates an array of photo IDs that need to be loaded.
-     */
-    private void obtainRequestsToLoad(HashSet<ImageRequest> requests) {
-        requests.clear();
-
-        /*
-         * Since the call is made from the loader thread, the map could be
-         * changing during the iteration. That's not really a problem:
-         * ConcurrentHashMap will allow those changes to happen without throwing
-         * exceptions. Since we may miss some requests in the situation of
-         * concurrent change, we will need to check the map again once loading
-         * is complete.
-         */
-        Iterator<ImageRequest> iterator = mPendingRequests.values().iterator();
-        while (iterator.hasNext()) {
-            ImageRequest key = iterator.next();
-            ImageHolder holder = mImageHolderCache.get(key);
-            if (holder == null || !holder.fresh) {
-                requests.add(key);
-            }
-        }
-    }
-
-//    /**
-//     * The thread that performs loading of photos from the database.
-//     */
-//    private class LoaderThread extends HandlerThread implements Callback {
-//        private static final int MESSAGE_PRELOAD_AVATARS = 0;
-//        private static final int MESSAGE_CONTINUE_PRELOAD = 1;
-//        private static final int MESSAGE_LOAD_IMAGES = 2;
-//        private static final int MESSAGE_NOTIFY_AVATAR_CHANGE = 3;
-//        private static final int MESSAGE_NOTIFY_MEDIA_IMAGE_CHANGE = 4;
-//        private static final int MESSAGE_NOTIFY_REMOTE_IMAGE_CHANGE = 5;
-//
-//        /**
-//         * A pause between preload batches that yields to the UI thread.
-//         */
-//        private static final int AVATAR_PRELOAD_DELAY = 50;
-//
-//        /**
-//         * Number of photos to preload per batch.
-//         */
-//        private static final int PRELOAD_BATCH = 25;
-//
-//        private final HashSet<ImageRequest> mRequests = new HashSet<ImageRequest>();
-////        private List<AvatarRequest> mPreloadRequests = new ArrayList<AvatarRequest>();
-//
-//        private Handler mLoaderThreadHandler;
-//
-//        private static final int PRELOAD_STATUS_NOT_STARTED = 0;
-//        private static final int PRELOAD_STATUS_IN_PROGRESS = 1;
-//        private static final int PRELOAD_STATUS_DONE = 2;
-//
-//        private int mPreloadStatus = PRELOAD_STATUS_NOT_STARTED;
-//
-//        public LoaderThread(ContentResolver resolver) {
-//            super(LOADER_THREAD_NAME);
-//        }
-//
-//        public void ensureHandler() {
-//            if (mLoaderThreadHandler == null) {
-//                mLoaderThreadHandler = new Handler(getLooper(), this);
-//            }
-//        }
-//
-////        /**
-////         * Kicks off preloading of the photos on the background thread.
-////         * Preloading will happen after a delay: we want to yield to the UI thread
-////         * as much as possible.
-////         * <p>
-////         * If preloading is already complete, does nothing.
-////         */
-////        public void startPreloading(List<AvatarRequest> requests) {
-////            ensureHandler();
-////
-////            mLoaderThreadHandler.sendMessage(mLoaderThreadHandler.obtainMessage(
-////                    MESSAGE_PRELOAD_AVATARS, requests));
-////        }
-//
-//        /**
-//         * Kicks off preloading of the next batch of photos on the background thread.
-//         * Preloading will happen after a delay: we want to yield to the UI thread
-//         * as much as possible.
-//         * <p>
-//         * If preloading is already complete, does nothing.
-//         */
-//        public void continuePreloading() {
-//            if (mPreloadStatus == PRELOAD_STATUS_DONE) {
-//                return;
-//            }
-//
-//            ensureHandler();
-//            if (mLoaderThreadHandler.hasMessages(MESSAGE_LOAD_IMAGES)) {
-//                return;
-//            }
-//
-//            mLoaderThreadHandler.sendEmptyMessageDelayed(MESSAGE_CONTINUE_PRELOAD,
-//                    AVATAR_PRELOAD_DELAY);
-//        }
-//
-//        /**
-//         * Sends a message to this thread to load requested photos.
-//         */
-//        public void requestLoading() {
-//            ensureHandler();
-//            mLoaderThreadHandler.removeMessages(MESSAGE_CONTINUE_PRELOAD);
-//            mLoaderThreadHandler.sendEmptyMessage(MESSAGE_LOAD_IMAGES);
-//        }
-//
-//        /**
-//         * Channels a change notification event through the loader thread to ensure
-//         * proper concurrency.
-//         */
-//        public void notifyAvatarChange(String gaiaId) {
-//            ensureHandler();
-//            Message msg = mLoaderThreadHandler.obtainMessage(MESSAGE_NOTIFY_AVATAR_CHANGE, gaiaId);
-//            mLoaderThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Channels a change notification event through the loader thread to ensure
-//         * proper concurrency.
-//         */
-//        public void notifyMediaImageChange(MediaImageChangeNotification notification) {
-//            ensureHandler();
-//            Message msg = mLoaderThreadHandler.obtainMessage(
-//                    MESSAGE_NOTIFY_MEDIA_IMAGE_CHANGE, notification);
-//            mLoaderThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Channels a change notification event through the loader thread to ensure
-//         * proper concurrency.
-//         */
-//        public void notifyRemoteImageChange(RemoteImageChangeNotification notification) {
-//            ensureHandler();
-//            Message msg = mLoaderThreadHandler.obtainMessage(
-//                    MESSAGE_NOTIFY_REMOTE_IMAGE_CHANGE, notification);
-//            mLoaderThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Receives the above message, loads photos and then sends a message
-//         * to the main thread to process them.
-//         */
-//        @Override
-//        public boolean handleMessage(Message msg) {
-//            try {
-//                switch (msg.what) {
-////                    case MESSAGE_PRELOAD_AVATARS:
-////                        @SuppressWarnings("unchecked")
-////                        List<AvatarRequest> requests = (List<AvatarRequest>) msg.obj;
-////                        mPreloadRequests.clear();
-////                        mPreloadRequests.addAll(requests);
-////                        mPreloadStatus = PRELOAD_STATUS_NOT_STARTED;
-////                        preloadAvatarsInBackground();
-////                        break;
-//                    case MESSAGE_CONTINUE_PRELOAD:
-////                        preloadAvatarsInBackground();
-//                        break;
-//                    case MESSAGE_LOAD_IMAGES:
-//                        loadImagesInBackground();
-//                        break;
-//                    case MESSAGE_NOTIFY_AVATAR_CHANGE:
-//                        sendMessageAvatarChange((String) msg.obj);
-//                        break;
-//                    case MESSAGE_NOTIFY_MEDIA_IMAGE_CHANGE:
-//                        sendMessageMediaImageChange((MediaImageChangeNotification) msg.obj);
-//                        break;
-//                    case MESSAGE_NOTIFY_REMOTE_IMAGE_CHANGE:
-//                        sendMessageRemoteImageChange((RemoteImageChangeNotification) msg.obj);
-//                        break;
-//                }
-//                return true;
-//            } catch (Throwable t) {
-//                Thread.getDefaultUncaughtExceptionHandler()
-//                    .uncaughtException(Thread.currentThread(), t);
-//                return false;
-//            }
-//        }
-//
-////        /**
-////         * The first time it is called, figures out which photos need to be preloaded.
-////         * Each subsequent call preloads the next batch of photos and requests
-////         * another cycle of preloading after a delay.  The whole process ends when
-////         * we either run out of photos to preload or fill up cache.
-////         */
-////        private void preloadAvatarsInBackground() {
-////            if (mPreloadStatus == PRELOAD_STATUS_DONE) {
-////                return;
-////            }
-////
-////            if (mPreloadStatus == PRELOAD_STATUS_NOT_STARTED) {
-////                if (mPreloadRequests.isEmpty()) {
-////                    mPreloadStatus = PRELOAD_STATUS_DONE;
-////                } else {
-////                    mPreloadStatus = PRELOAD_STATUS_IN_PROGRESS;
-////                }
-////                continuePreloading();
-////                return;
-////            }
-////
-////            if (mImageHolderCache.size() > mImageHolderCacheRedZoneBytes) {
-////                mPreloadStatus = PRELOAD_STATUS_DONE;
-////                return;
-////            }
-////
-////            mRequests.clear();
-////
-////            int count = 0;
-////            int preloadSize = mPreloadRequests.size();
-////            while (preloadSize > 0 && mRequests.size() < PRELOAD_BATCH) {
-////                preloadSize--;
-////                AvatarRequest request = mPreloadRequests.get(preloadSize);
-////                mPreloadRequests.remove(preloadSize);
-////
-////                if (mImageHolderCache.get(request) == null) {
-////                    mRequests.add(request);
-////                    count++;
-////                }
-////            }
-////
-////            loadImagesFromDatabase(true);
-////
-////            if (preloadSize == 0) {
-////                mPreloadStatus = PRELOAD_STATUS_DONE;
-////            }
-////
-////            if (EsLog.isLoggable(TAG, Log.INFO)) {
-////                Log.v(TAG, "Preloaded " + count + " avatars. "
-////                        + "Cache size (bytes): " + mImageHolderCache.size());
-////            }
-////
-////            // Ask to preload the next batch.
-////            continuePreloading();
-////        }
-//
-//        /**
-//         * Forwards the change notification event to the main thread.
-//         */
-//        private void sendMessageAvatarChange(String gaiaId) {
-//            Message msg = mMainThreadHandler.obtainMessage(MESSAGE_AVATAR_CHANGED, gaiaId);
-//            mMainThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Forwards the change notification event to the main thread.
-//         */
-//        private void sendMessageMediaImageChange(MediaImageChangeNotification notification) {
-//            deliverImage(notification.request, notification.imageBytes, true, false);
-//            Message msg = mMainThreadHandler.obtainMessage(
-//                    MESSAGE_MEDIA_IMAGE_CHANGED, notification);
-//            mMainThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Forwards the change notification event to the main thread.
-//         */
-//        private void sendMessageRemoteImageChange(RemoteImageChangeNotification notification) {
-//            deliverImage(notification.request, notification.imageBytes, true, false);
-//            Message msg = mMainThreadHandler.obtainMessage(
-//                    MESSAGE_REMOTE_IMAGE_CHANGED, notification);
-//            mMainThreadHandler.sendMessage(msg);
-//        }
-//
-//        /**
-//         * Loads photos from the database, puts them in cache and then notifies the UI thread
-//         * that they have been loaded.
-//         */
-//        private void loadImagesInBackground() {
-//            obtainRequestsToLoad(mRequests);
-//            loadImagesFromDatabase(false);
-//            continuePreloading();
-//        }
-//
-////        /**
-////         * Loads photos from the database, puts them in cache and then notifies the UI thread
-////         * that they have been loaded.
-////         */
-////        private void loadImagesFromDatabase(boolean preloading) {
-////            int count = mRequests.size();
-////            if (count == 0) {
-////                return;
-////            }
-////
-////            // Remove loaded photos from the preload queue: we don't want
-////            // the preloading process to load them again.
-////            if (!preloading && mPreloadStatus == PRELOAD_STATUS_IN_PROGRESS) {
-////                mPreloadRequests.removeAll(mRequests);
-////                if (mPreloadRequests.isEmpty()) {
-////                    mPreloadStatus = PRELOAD_STATUS_DONE;
-////                }
-////            }
-////
-////            ArrayList<AvatarRequest> avatarRequests = null;
-////            ArrayList<MediaImageRequest> mediaRequests = null;
-////            ArrayList<EventThemeImageRequest> eventThemeRequests = null;
-////            ArrayList<ImageRequest> remoteRequests = null;
-////
-////            for (ImageRequest request : mRequests) {
-////                if (request instanceof AvatarRequest) {
-////                    if (avatarRequests == null) {
-////                        avatarRequests = new ArrayList<AvatarRequest>();
-////                    }
-////                    avatarRequests.add((AvatarRequest) request);
-////                } else if (request instanceof MediaImageRequest) {
-////                    if (mediaRequests == null) {
-////                        mediaRequests = new ArrayList<MediaImageRequest>();
-////                    }
-////                    mediaRequests.add((MediaImageRequest) request);
-////                } else if (request instanceof EventThemeImageRequest) {
-////                    if (eventThemeRequests == null) {
-////                        eventThemeRequests = new ArrayList<EventThemeImageRequest>();
-////                    }
-////                    eventThemeRequests.add((EventThemeImageRequest) request);
-////                } else {
-////                    if (remoteRequests == null) {
-////                        remoteRequests = new ArrayList<ImageRequest>();
-////                    }
-////                    remoteRequests.add(request);
-////                }
-////            }
-////
-////            if (mediaRequests != null) {
-////                Map<MediaImageRequest, byte[]> avatars = EsPostsData.loadMedia(
-////                        mContext, mediaRequests);
-////
-////                for (Entry<MediaImageRequest, byte[]> entry : avatars.entrySet()) {
-////                    MediaImageRequest request = entry.getKey();
-////                    deliverImage(request, entry.getValue(), true, preloading);
-////                    mRequests.remove(request);
-////                }
-////            }
-////
-////            if (avatarRequests != null) {
-////                Map<AvatarRequest, byte[]> avatars = EsAvatarData.loadAvatars(
-////                        mContext, avatarRequests);
-////
-////                for (Entry<AvatarRequest, byte[]> entry : avatars.entrySet()) {
-////                    AvatarRequest request = entry.getKey();
-////                    deliverImage(request, entry.getValue(), true, preloading);
-////                    mRequests.remove(request);
-////                }
-////            }
-////
-////            if (eventThemeRequests != null) {
-////                for (EventThemeImageRequest request : eventThemeRequests) {
-////                    byte[] themeBytes = EsEventData.loadEventTheme(mContext, request);
-////                    if (themeBytes != null) {
-////                        deliverImage(request, themeBytes, true, false);
-////                        mRequests.remove(request);
-////                    }
-////                }
-////            }
-////
-////            // NOTE: Do not use the same pattern as other images for the following image.
-////            // Since all of these photos are "local" [either because they're physically
-////            // stored on the device or because they're available through the Picasa
-////            // content provider], there is no need to store them in the database. Just
-////            // throw all of the requests into a loader thread and be done with it.
-////            if (remoteRequests != null) {
-////                final int requestCount = remoteRequests.size();
-////                for (int i = 0; i < requestCount; i++) {
-////                    final ImageRequest request = remoteRequests.get(i);
-////
-////                    // Only if we still need to load
-////                    if (mPendingRequests.containsValue(request)) {
-////                        RemoteImageLoader.downloadImage(mContext, request);
-////                    }
-////                }
-////            }
-////
-////            // Remaining photos were not found in the database - mark the cache accordingly.
-////            for (ImageRequest request : mRequests) {
-////                deliverImage(request, null, false, preloading);
-////            }
-////
-////            mMainThreadHandler.sendEmptyMessage(MESSAGE_IMAGES_LOADED);
-////        }
-//    }
-}
diff --git a/src/com/android/mail/photo/util/ImageProxyUtil.java b/src/com/android/mail/photo/util/ImageProxyUtil.java
deleted file mode 100644
index 11370ad..0000000
--- a/src/com/android/mail/photo/util/ImageProxyUtil.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.util;
-
-import android.net.Uri;
-
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Useful Image Proxy url manipulation routines.
- */
-public class ImageProxyUtil {
-    private static final Pattern PROXY_HOSTED_IMAGE_URL_RE =
-            Pattern.compile("^(((http(s)?):)?\\/\\/"
-            + "images(\\d)?-.+-opensocial\\.googleusercontent\\.com\\/gadgets\\/proxy\\?)");
-
-    /** Default container, if we don't already have one */
-    static final String DEFAULT_CONTAINER = "esmobile";
-
-    static final String PROXY_DOMAIN_PREFIX = "images";
-    static final String PROXY_DOMAIN_SUFFIX = "-opensocial.googleusercontent.com";
-    static final String PROXY_PATH = "/gadgets/proxy";
-    static final String PARAM_URL = "url";
-    static final String PARAM_CONTAINER = "container";
-    static final String PARAM_GADGET = "gadget";
-    static final String PARAM_REWRITE_MIME = "rewriteMime";
-    static final String PARAM_REFRESH = "refresh";
-    static final String PARAM_HEIGHT = "resize_h";
-    static final String PARAM_WIDTH = "resize_w";
-    static final String PARAM_QUALITY = "resize_q";
-    static final String PARAM_NO_EXPAND = "no_expand";
-    static final String PARAM_FALLBACK_URL = "fallback_url";
-    static final int PROXY_COUNT = 3;
-    static int sProxyIndex;
-
-    public static final int ORIGINAL_SIZE = -1;
-
-    /**
-     * Add size options to the given url.
-     *
-     * @param size the image size
-     * @param url the url to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static String setImageUrlSize(int size, String url) {
-        if (url == null) {
-            return url;
-        }
-
-        final String proxyUrl;
-        if (!isProxyHostedUrl(url)) {
-            proxyUrl = createProxyUrl();
-        } else {
-            proxyUrl = url;
-            url = null;
-        }
-        final Uri proxyUri = Uri.parse(proxyUrl);
-        return setImageUrlSizeOptions(size, size, proxyUri, url).toString();
-    }
-
-
-    /**
-     * Add size options to the given url.
-     *
-     * @param width the image width
-     * @param height the image height
-     * @param url the url to apply the options to
-     * @return a {@code Uri} containting the new image url with options.
-     */
-    public static String setImageUrlSize(int width, int height, String url) {
-        if (url == null) {
-            return url;
-        }
-
-        final String proxyUrl;
-        if (!isProxyHostedUrl(url)) {
-            proxyUrl = createProxyUrl();
-        } else {
-            proxyUrl = url;
-            url = null;
-        }
-        final Uri proxyUri = Uri.parse(proxyUrl);
-        return setImageUrlSizeOptions(width, height, proxyUri, url).toString();
-    }
-
-    /**
-     * Returns a default proxy URL.
-     */
-    private static String createProxyUrl() {
-        StringBuffer proxy = new StringBuffer();
-        proxy.append("http://")
-            .append(PROXY_DOMAIN_PREFIX)
-            .append(getNextProxyIndex())
-            .append("-")
-            .append(DEFAULT_CONTAINER)
-            .append(PROXY_DOMAIN_SUFFIX)
-            .append(PROXY_PATH);
-        return proxy.toString();
-    }
-
-    /**
-     * Returns the next proxy index.
-     */
-    private static synchronized int getNextProxyIndex() {
-        int toReturn = ++sProxyIndex;
-        sProxyIndex %= PROXY_COUNT;
-        return toReturn;
-    }
-
-    /**
-     * Add image url options to the given url.
-     *
-     * @param width the image width
-     * @param height the image height
-     * @param proxyUri the uri to apply the options to
-     * @return a {@code Uri} containing the image url with the width and height set.
-     */
-    public static Uri setImageUrlSizeOptions(int width, int height, Uri proxyUri, String imageUrl) {
-        Uri.Builder proxyUriBuilder;
-        Uri newProxyUri;
-
-        proxyUriBuilder = Uri.EMPTY.buildUpon();
-        proxyUriBuilder.authority(proxyUri.getAuthority());
-        proxyUriBuilder.scheme(proxyUri.getScheme());
-        proxyUriBuilder.path(proxyUri.getPath());
-        // Set these here to override any settings in the source proxy URI
-        if (width != ORIGINAL_SIZE && height != ORIGINAL_SIZE) {
-            proxyUriBuilder.appendQueryParameter(PARAM_WIDTH, Integer.toString(width));
-            proxyUriBuilder.appendQueryParameter(PARAM_HEIGHT, Integer.toString(height));
-            proxyUriBuilder.appendQueryParameter(PARAM_NO_EXPAND, "1");
-        }
-
-        newProxyUri = proxyUriBuilder.build();
-
-        final Set<String> paramNames = getQueryParameterNames(proxyUri);
-        for (String key : paramNames) {
-            if (newProxyUri.getQueryParameter(key) != null) {
-                continue;
-            }
-
-            proxyUriBuilder = newProxyUri.buildUpon();
-            if (PARAM_URL.equals(key)) {
-                // Ensure there's only one url parameter
-                proxyUriBuilder.appendQueryParameter(PARAM_URL,
-                        proxyUri.getQueryParameter(PARAM_URL));
-
-            } else if ((width == ORIGINAL_SIZE || height == ORIGINAL_SIZE) &&
-                    (PARAM_WIDTH.equals(key) || PARAM_HEIGHT.equals(key) ||
-                    PARAM_NO_EXPAND.equals(key))) {
-                // Don't allow width / height / no-expand parameters if we ask for a full-size image
-                continue;
-
-            } else {
-                final List<String> values = proxyUri.getQueryParameters(key);
-                for (String value : values) {
-                    proxyUriBuilder.appendQueryParameter(key, value);
-                }
-            }
-            newProxyUri = proxyUriBuilder.build();
-        }
-
-        // The following parameters are mandatory; make sure the URL has them
-        if (imageUrl != null && newProxyUri.getQueryParameter(PARAM_URL) == null) {
-            proxyUriBuilder = newProxyUri.buildUpon();
-            proxyUriBuilder.appendQueryParameter(PARAM_URL, imageUrl);
-            newProxyUri = proxyUriBuilder.build();
-        }
-        if (newProxyUri.getQueryParameter(PARAM_CONTAINER) == null) {
-            proxyUriBuilder = newProxyUri.buildUpon();
-            proxyUriBuilder.appendQueryParameter(PARAM_CONTAINER, DEFAULT_CONTAINER);
-            newProxyUri = proxyUriBuilder.build();
-        }
-        if (newProxyUri.getQueryParameter(PARAM_GADGET) == null) {
-            proxyUriBuilder = newProxyUri.buildUpon();
-            proxyUriBuilder.appendQueryParameter(PARAM_GADGET, "a");
-            newProxyUri = proxyUriBuilder.build();
-        }
-        if (newProxyUri.getQueryParameter(PARAM_REWRITE_MIME) == null) {
-            proxyUriBuilder = newProxyUri.buildUpon();
-            proxyUriBuilder.appendQueryParameter(PARAM_REWRITE_MIME, "image/*");
-            newProxyUri = proxyUriBuilder.build();
-        }
-
-        return newProxyUri;
-    }
-
-    /**
-     * Backwards-compatible implementation of
-     * {@link Uri#getQueryParameterNames()}.
-     */
-    private static Set<String> getQueryParameterNames(Uri uri) {
-        if (uri.isOpaque()) {
-            throw new UnsupportedOperationException("This isn't a hierarchical URI.");
-        }
-
-        String query = uri.getEncodedQuery();
-        if (query == null) {
-            return Collections.emptySet();
-        }
-
-        Set<String> names = new LinkedHashSet<String>();
-        int start = 0;
-        do {
-            int next = query.indexOf('&', start);
-            int end = (next == -1) ? query.length() : next;
-
-            int separator = query.indexOf('=', start);
-            if (separator > end || separator == -1) {
-                separator = end;
-            }
-
-            String name = query.substring(start, separator);
-            names.add(Uri.decode(name));
-
-            // Move start to end of name.
-            start = end + 1;
-        } while (start < query.length());
-
-        return Collections.unmodifiableSet(names);
-    }
-
-    /**
-     * Checks if the host is a valid FIFE host.
-     *
-     * @param url an image url to check
-     *
-     * @return {@code true} iff the url has a valid FIFE host
-     */
-    public static boolean isProxyHostedUrl(String url) {
-        if (url == null) {
-            return false;
-        }
-
-        final Matcher matcher = PROXY_HOSTED_IMAGE_URL_RE.matcher(url);
-        return matcher.find();
-    }
-
-    /**
-     * Checks if the host is a valid FIFE host.
-     *
-     * @param uri an image url to check
-     *
-     * @return {@code true} iff the url has a valid FIFE host
-     */
-    public static boolean isProxyHostedUri(Uri uri) {
-        return isProxyHostedUrl(uri.toString());
-    }
-}
diff --git a/src/com/android/mail/photo/util/ImageUtils.java b/src/com/android/mail/photo/util/ImageUtils.java
index 4143d2d..98edacb 100644
--- a/src/com/android/mail/photo/util/ImageUtils.java
+++ b/src/com/android/mail/photo/util/ImageUtils.java
@@ -17,115 +17,32 @@
 

 package com.android.mail.photo.util;

 

-import android.app.Dialog;

-import android.app.ProgressDialog;

 import android.content.ContentResolver;

-import android.content.ContentValues;

-import android.content.Context;

-import android.content.res.Resources;

-import android.database.Cursor;

 import android.graphics.Bitmap;

-import android.graphics.Bitmap.CompressFormat;

-import android.graphics.Bitmap.Config;

 import android.graphics.BitmapFactory;

-import android.graphics.Canvas;

-import android.graphics.Matrix;

-import android.graphics.Paint;

 import android.graphics.Point;

 import android.graphics.Rect;

-import android.graphics.drawable.Drawable;

-import android.media.ExifInterface;

 import android.net.Uri;

 import android.os.Build;

-import android.os.Environment;

-import android.provider.MediaStore.Images;

-import android.provider.MediaStore.Images.ImageColumns;

-import android.provider.MediaStore.Images.Thumbnails;

-import android.provider.MediaStore.MediaColumns;

-import android.text.TextUtils;

-import android.util.Base64;

 import android.util.Log;

 

-import com.android.mail.R;

 import com.android.mail.photo.PhotoViewActivity;

 

-import java.io.ByteArrayInputStream;

-import java.io.ByteArrayOutputStream;

-import java.io.File;

-import java.io.FileInputStream;

 import java.io.FileNotFoundException;

 import java.io.IOException;

 import java.io.InputStream;

-import java.io.OutputStream;

-import java.text.SimpleDateFormat;

-import java.util.Date;

 

 /**

  * Image utilities

  */

 public class ImageUtils {

-    /** Specifies no background colour should be added during image resizing */

-    public static int NO_COLOR = 0;

-

-    public static final int INSERT_PHOTO_DIALOG_ID = R.id.dialog_insert_photo;

-    

-    // added from EsService

-    public static final int CROP_NONE = 0;

-    public static final int CROP_SQUARE = 1;

-    public static final int CROP_WIDE = 2;

-

-    private static int MICRO_KIND_MAX_DIMENSION = 0;

-    private static int MINI_KIND_MAX_DIMENSION = 0;

-

-    private static int DEFAULT_JPEG_QUALITY = 90;

-

     // Logging

     private static final String TAG = "ImageUtils";

 

-    // Paints and modes

-    private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG);

-

-    /** The paint used for cropped photos */

-    private static final Paint sCropPaint;

-    static {

-        sCropPaint = new Paint();

-        sCropPaint.setAntiAlias(true);

-        sCropPaint.setFilterBitmap(true);

-        sCropPaint.setDither(true);

-    }

-

-    private static final Paint sOutStrokePaint = new Paint();

-    static {

-        sOutStrokePaint.setStrokeWidth(1);

-        sOutStrokePaint.setStyle(Paint.Style.STROKE);

-        sOutStrokePaint.setColor(0xff999999);

-    }

-

-    private static final Paint sInStrokePaint = new Paint();

-    static {

-        sInStrokePaint.setStrokeWidth(1);

-        sInStrokePaint.setStyle(Paint.Style.STROKE);

-        sInStrokePaint.setColor(0xfff0f0f0);

-    }

-

     /** Minimum class memory class to use full-res photos */

     private final static long MIN_NORMAL_CLASS = 32;

     /** Minimum class memory class to use small photos */

     private final static long MIN_SMALL_CLASS = 24;

-    public static final boolean sUseLowResImages;

-    static {

-        if (Build.VERSION.SDK_INT >= 11) {

-            // On HC and beyond, assume devices are more capable

-            sUseLowResImages = false;

-        } else {

-            if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {

-                sUseLowResImages = false;

-            } else {

-                // If we're not in the small class, use low-res [i.e. RGB_565] photos

-                sUseLowResImages = true;

-            }

-        }

-    }

 

     public static enum ImageSize {

         EXTRA_SMALL,

@@ -153,124 +70,10 @@
     }

 

     /**

-     * Interface for when a dialog informing about a camera photo insertion

-     * should be shown or hidden.

+     * @return true if the MimeType type is image

      */

-    public interface InsertCameraPhotoDialogDisplayer {

-        public void showInsertCameraPhotoDialog();

-        public void hideInsertCameraPhotoDialog();

-    }

-

-    /**

-     * This class cannot be instantiated

-     */

-    private ImageUtils() {

-    }

-

-

-    /**

-     * Parses an image from a byte array. May return either a Bitmap or

-     * a {@link Drawable}.

-     *

-     * @param data byte array of compressed image data

-     * @return The decoded bitmap or {@link Drawable}, or null if the image could not be decoded.

-     */

-    public static Object decodeMedia(byte[] data) {

-        try {

-            if (GifDrawable.isGif(data)) {

-                return new GifDrawable(data);

-            } else {

-                return BitmapFactory.decodeByteArray(data, 0, data.length);

-            }

-        } catch (OutOfMemoryError oome) {

-            Log.e(TAG, "ImageUtils#decodeMedia(byte[]) threw an OOME", oome);

-            return null;

-        }

-    }

-

-    /**

-     * Wrapper around {@link BitmapFactory#decodeByteArray(byte[], int, int)}

-     * that returns {@code null} on {@link OutOfMemoryError}.

-     *

-     * @param data byte array of compressed image data

-     * @param offset offset into imageData for where the decoder should begin

-     *               parsing.

-     * @param length the number of bytes, beginning at offset, to parse

-     * @return The decoded bitmap, or null if the image could not be decode.

-     */

-    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {

-        try {

-            return BitmapFactory.decodeByteArray(data, offset, length);

-        } catch (OutOfMemoryError oome) {

-            Log.e(TAG, "ImageUtils#decodeByteArray(byte[], int, int) threw an OOME", oome);

-            return null;

-        }

-    }

-

-    /**

-     * Wrapper around {@link BitmapFactory#decodeByteArray(byte[], int, int,

-     * BitmapFactory.Options)} that returns {@code null} on {@link

-     * OutOfMemoryError}.

-     *

-     * @param data byte array of compressed image data

-     * @param offset offset into imageData for where the decoder should begin

-     *               parsing.

-     * @param length the number of bytes, beginning at offset, to parse

-     * @param opts null-ok; Options that control downsampling and whether the

-     *             image should be completely decoded, or just is size returned.

-     * @return The decoded bitmap, or null if the image could not be decode.

-     */

-    public static Bitmap decodeByteArray(byte[] data, int offset, int length,

-            BitmapFactory.Options opts) {

-        try {

-            return BitmapFactory.decodeByteArray(data, offset, length, opts);

-        } catch (OutOfMemoryError oome) {

-            Log.e(TAG, "ImageUtils#decodeByteArray(byte[], int, int, Options) threw an OOME", oome);

-            return null;

-        }

-    }

-

-    /**

-     * Wrapper around {@link BitmapFactory#decodeResource(Resources, int)}

-     * that returns {@code null} on {@link OutOfMemoryError}.

-     *

-     * @param res The resources object containing the image data

-     * @param id The resource id of the image data

-     * @return The decoded bitmap, or null if the image could not be decode.

-     */

-    public static Bitmap decodeResource(Resources res, int id) {

-        try {

-            return BitmapFactory.decodeResource(res, id);

-        } catch (OutOfMemoryError oome) {

-            Log.e(TAG, "ImageUtils#decodeResource(Resources, int) threw an OOME", oome);

-            return null;

-        }

-    }

-

-    /**

-     * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,

-     * BitmapFactory.Options)} that returns {@code null} on {@link

-     * OutOfMemoryError}.

-     *

-     * @param is The input stream that holds the raw data to be decoded into a

-     *           bitmap.

-     * @param outPadding If not null, return the padding rect for the bitmap if

-     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If

-     *                   no bitmap is returned (null) then padding is

-     *                   unchanged.

-     * @param opts null-ok; Options that control downsampling and whether the

-     *             image should be completely decoded, or just is size returned.

-     * @return The decoded bitmap, or null if the image data could not be

-     *         decoded, or, if opts is non-null, if opts requested only the

-     *         size be returned (in opts.outWidth and opts.outHeight)

-     */

-    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {

-        try {

-            return BitmapFactory.decodeStream(is, outPadding, opts);

-        } catch (OutOfMemoryError oome) {

-            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);

-            return null;

-        }

+    public static boolean isImageMimeType(String mimeType) {

+        return mimeType != null && mimeType.startsWith("image/");

     }

 

     /**

@@ -314,832 +117,29 @@
     }

 

     /**

-     * Creates a bitmap from the given bytes at the specified dimension and with the

-     * specified crop. Sub-sample as necessary.

+     * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,

+     * BitmapFactory.Options)} that returns {@code null} on {@link

+     * OutOfMemoryError}.

      *

-     * TODO(toddke) Currently, we only perform the wide crop in this method. The square

-     * crop is already handled via the FIFE / Image Proxy URLs. When the photo cache and

-     * image cache are merged, we'll need to support square crop as well.

+     * @param is The input stream that holds the raw data to be decoded into a

+     *           bitmap.

+     * @param outPadding If not null, return the padding rect for the bitmap if

+     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If

+     *                   no bitmap is returned (null) then padding is

+     *                   unchanged.

+     * @param opts null-ok; Options that control downsampling and whether the

+     *             image should be completely decoded, or just is size returned.

+     * @return The decoded bitmap, or null if the image data could not be

+     *         decoded, or, if opts is non-null, if opts requested only the

+     *         size be returned (in opts.outWidth and opts.outHeight)

      */

-    public static Bitmap createBitmap(byte[] imageBytes, int width, int height, int cropType) {

-        if (imageBytes == null || imageBytes.length == 0) {

-            return null;

-        }

-

-        final ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);

-        final boolean useLowResImages = ImageUtils.sUseLowResImages;

+    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {

         try {

-            final BitmapFactory.Options opts = new BitmapFactory.Options();

-            final Point bounds = getImageBounds(imageBytes);

-

-            if (Log.isLoggable(TAG, Log.DEBUG)) {

-                Log.d(TAG, "PhotoCache#createBitmap; w: " +

-                        bounds.x + ", h: " + bounds.y + ", max: " + width);

-            }

-            opts.inSampleSize = Math.max(bounds.x / width, bounds.y / height);

-            if (useLowResImages) {

-                opts.inPreferredConfig = Config.RGB_565;

-            }

-

-            final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);

-            if (decodedBitmap == null) {

-                return null;

-            }

-

-            final Bitmap croppedBitmap;

-            if (cropType == CROP_WIDE) { // changed from EsService.CROP_WIDE

-                croppedBitmap = cropWideBitmap(decodedBitmap, width, height);

-                decodedBitmap.recycle();

-

-                if (croppedBitmap == null) {

-                    return null;

-                }

-            } else {

-                croppedBitmap = decodedBitmap;

-            }

-

-            if (useLowResImages) {

-                final Bitmap lowResBitmap = ImageUtils.getLowResBitmap(croppedBitmap);

-                if (lowResBitmap != croppedBitmap) {

-                    croppedBitmap.recycle();

-                }

-                return lowResBitmap;

-            } else {

-                return croppedBitmap;

-            }

-        } catch (OutOfMemoryError e) {

-            // Do nothing - the photo will appear to be missing

-        } finally {

-            try {

-                inputStream.close();

-            } catch (IOException ignore) {

-            }

-        }

-        return null;

-    }

-

-    /**

-     * Crops the given bitmap according to the {@link EsService#CROP_WIDE} style. The

-     * center of the bitmap is used to create a new bitmap of exactly width x height

-     * pixels, maintaining the original aspect ratio. The original bitmap will be

-     * cropped and/or enlarged as necessary.

-     */

-    private static Bitmap cropWideBitmap(Bitmap inputBitmap, int width, int height) {

-        final Rect srcRect;

-

-        final int srcWidth = inputBitmap.getWidth();

-        final int srcHeight = inputBitmap.getHeight();

-        final int dstWidth  = width;

-        final int dstHeight = height;

-

-        if (srcWidth == dstWidth && srcHeight == dstHeight) {

-            // Photo is exactly the same size as the on-screen image

-            srcRect = new Rect(0, 0, srcWidth, srcHeight);

-        } else {

-            // create a source rectangle of the same aspect ratio as the requested size.

-            int cropWidth = srcWidth;

-            int cropHeight = srcHeight;

-            if (srcWidth * dstHeight > srcHeight * dstWidth) {

-                // the input bitmap is a wider aspect ratio.  Crop the sides.

-                cropWidth = srcHeight * dstWidth / dstHeight;

-            } else {

-                // The input bitmap is a taller aspect ratio.  Crop the top and bottom.

-                cropHeight = srcWidth * dstHeight / dstWidth;

-            }

-

-            final int left = (srcWidth - cropWidth) / 2;

-            final int top = (srcHeight - cropHeight) / 2;

-            srcRect = new Rect(left, top, left + cropWidth, top + cropHeight);

-        }

-

-        // Create the new bitmap

-        final Bitmap.Config bitmapConfig =

-                ImageUtils.sUseLowResImages ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

-        final Bitmap bitmap = Bitmap.createBitmap(width, height, bitmapConfig);

-        if (bitmap == null) {

+            return BitmapFactory.decodeStream(is, outPadding, opts);

+        } catch (OutOfMemoryError oome) {

+            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);

             return null;

         }

-

-        final Canvas canvas = new Canvas(bitmap);

-        final Rect dstRect = new Rect(0, 0, width, height);

-

-        synchronized (sCropPaint) {

-            canvas.drawBitmap(inputBitmap, srcRect, dstRect, sCropPaint);

-        }

-

-        return bitmap;

-    }

-

-    /**

-     * Gets the image bounds

-     */

-    private static Point getImageBounds(byte[] imageBytes) {

-        final BitmapFactory.Options opts = new BitmapFactory.Options();

-        final ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);

-

-        try {

-            opts.inJustDecodeBounds = true;

-            decodeStream(inputStream, null, opts);

-            return new Point(opts.outWidth, opts.outHeight);

-        } finally {

-            try {

-                inputStream.close();

-            } catch (IOException ignore) {

-            }

-        }

-    }

-

-    /**

-     * Create a center-cropped bitmap from a uri.

-     *

-     * @param resolver The ContentResolver

-     * @param uri The uri

-     * @param width The width of the output bitmap

-     * @param height The height of the output bitmap

-     *

-     * @return the new bitmap

-     */

-    public static Bitmap createCroppedBitmap(ContentResolver resolver, Uri uri,

-            int width, int height) {

-        try {

-            InputStream inputStream = resolver.openInputStream(uri);

-            final BitmapFactory.Options opts = new BitmapFactory.Options();

-            opts.inJustDecodeBounds = true;

-            decodeStream(inputStream, null, opts);

-            inputStream.close();

-

-            // use Math.min() here to ensure that each of the image dimensions are

-            // >= the target size

-            inputStream = resolver.openInputStream(uri);

-            opts.inJustDecodeBounds = false;

-            opts.inSampleSize = Math.min(opts.outWidth / width, opts.outHeight / height);

-            Bitmap srcBitmap = decodeStream(inputStream, null, opts);

-            inputStream.close();

-            if (srcBitmap == null) {

-                return null;

-            }

-            final int srcWidth = srcBitmap.getWidth();

-            final int srcHeight = srcBitmap.getHeight();

-

-            if (srcWidth == width && srcHeight == height) {

-                return srcBitmap;

-            }

-

-            Bitmap destBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

-            if (destBitmap == null) {

-                srcBitmap.recycle();

-                return null;

-            }

-

-            final Canvas canvas = new Canvas(destBitmap);

-            int croppedWidth = srcWidth;

-            int croppedHeight = srcHeight;

-            // We want to take the center part of the image with the same aspect

-            // ratio as the target, and crop the rest.  The same behavior as CENTER_CROP.

-            if (srcWidth * height > srcHeight * width) {

-                // The input bitmap is a wider aspect ratio.  Crop the sides.

-                croppedWidth = srcHeight * width / height;

-            } else {

-                // The input bitmap is a taller aspect ratio.  Crop the top and bottom.

-                croppedHeight = srcWidth * height / width;

-            }

-            final int left = (srcWidth - croppedWidth) / 2;

-            final int top = (srcHeight - croppedHeight) / 2;

-            final Rect src = new Rect(left, top, left + croppedWidth, top + croppedHeight);

-            synchronized (sResizePaint) {

-                canvas.drawBitmap(srcBitmap, src, new Rect(0, 0, width, height), sResizePaint);

-            }

-            srcBitmap.recycle();

-

-            // correct orientation, as necessary

-            return rotateBitmap(resolver, uri, destBitmap);

-        } catch (FileNotFoundException exception) {

-            return null;

-        } catch (IOException exception) {

-            return null;

-        }

-    }

-

-    /**

-     * Returns the maximum dimension in pixels for a given MediaStore.Images.Thumbnails kind.

-     *

-     * @param context The context

-     * @param kind MICRO_KIND or MINI_KIND

-     *

-     * @return maxDimension in pixels

-     */

-    public static int getMaxThumbnailDimension(Context context, int kind) {

-        // determine max dimension based on kind

-        final int maxDimension;

-        switch (kind) {

-            case Thumbnails.MICRO_KIND:

-                maxDimension = getThumbnailSize(context, Thumbnails.MICRO_KIND);

-                break;

-

-            case Thumbnails.MINI_KIND:

-                maxDimension = getThumbnailSize(context, Thumbnails.MINI_KIND);

-                break;

-

-            default:

-                if (Log.isLoggable(TAG, Log.DEBUG)) {

-                    Log.d(TAG, "illegal kind=" + kind + " specified; using MINI_KIND");

-                }

-                maxDimension = getThumbnailSize(context, Thumbnails.MINI_KIND);

-                break;

-        }

-        return maxDimension;

-    }

-

-    /**

-     * Convert thumbnail dimensions to pixels

-     *

-     * @param context The context

-     * @param kind The kind

-     *

-     * @return The size of the thumbnail in pixels

-     */

-    public static int getThumbnailSize(Context context, int kind) {

-        switch (kind) {

-            case Thumbnails.MICRO_KIND: {

-                if (MICRO_KIND_MAX_DIMENSION == 0) {

-                    MICRO_KIND_MAX_DIMENSION = context.getResources().getDimensionPixelSize(

-                            R.dimen.micro_kind_max_dimension);

-                }

-                return MICRO_KIND_MAX_DIMENSION;

-            }

-

-            case Thumbnails.MINI_KIND:

-            default: {

-                if (MINI_KIND_MAX_DIMENSION == 0) {

-                    MINI_KIND_MAX_DIMENSION = context.getResources().getDimensionPixelSize(

-                            R.dimen.mini_kind_max_dimension);

-                }

-                return MINI_KIND_MAX_DIMENSION;

-            }

-        }

-    }

-

-    /**

-     * Scale a bitmap to a square bitmap

-     *

-     * @param imageBytes The input bitmap

-     * @param size The width and height

-     *

-     * @return The new bitmap

-     */

-    public static byte[] resizeToSquareBitmap(byte[] imageBytes, int size) {

-        return resizeToSquareBitmap(imageBytes, size, NO_COLOR);

-    }

-

-    /**

-     * Scale a bitmap to a square bitmap

-     *

-     * @param imageBytes The input bitmap

-     * @param size The width and height

-     * @param backgroundColor The background color that should be used for translucent avatars.

-     *

-     * @return The new bitmap

-     */

-    public static byte[] resizeToSquareBitmap(byte[] imageBytes, int size, int backgroundColor) {

-        if (imageBytes == null) {

-            return imageBytes;

-        }

-

-        final BitmapFactory.Options dbo = new BitmapFactory.Options();

-        dbo.inJustDecodeBounds = true;

-        decodeByteArray(imageBytes, 0, imageBytes.length, dbo);

-

-        int nativeWidth = dbo.outWidth;

-        int nativeHeight = dbo.outHeight;

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "resizeToSquareBitmap: Input: " + nativeWidth + "x" + nativeHeight

-                    + ", resize to: " + size);

-        }

-

-        Bitmap bitmap;

-        int sampleSize = Math.min(nativeWidth / size, nativeHeight / size);

-        if (sampleSize > 1) {

-            BitmapFactory.Options options = new BitmapFactory.Options();

-            options.inSampleSize = sampleSize;

-            bitmap = decodeByteArray(imageBytes, 0, imageBytes.length, options);

-        } else {

-            bitmap = decodeByteArray(imageBytes, 0, imageBytes.length);

-        }

-

-        if (bitmap == null) {

-            return null;

-        }

-

-        Bitmap scaledBitmap = resizeToSquareBitmap(bitmap, size, backgroundColor);

-        bitmap.recycle();

-

-        if (scaledBitmap == null) {

-            return null;

-        }

-

-        ByteArrayOutputStream stream = new ByteArrayOutputStream();

-        scaledBitmap.compress(CompressFormat.JPEG, 80, stream);

-        scaledBitmap.recycle();

-        scaledBitmap = null;

-

-        return stream.toByteArray();

-    }

-

-    /**

-     * Scale a bitmap to a square bitmap

-     *

-     * @param inputBitmap The input bitmap

-     * @param size The width and height

-     *

-     * @return The new bitmap

-     */

-    public static Bitmap resizeToSquareBitmap(Bitmap inputBitmap, int size) {

-        return resizeToSquareBitmap(inputBitmap, size, NO_COLOR);

-    }

-

-    /**

-     * Scale a bitmap to a square bitmap

-     *

-     * @param inputBitmap The input bitmap

-     * @param size The width and height

-     * @param backgroundColor The solid color used to paint the image background. If

-     *              {@link #NO_COLOR}, no background will be painted.

-     *

-     * @return The new bitmap

-     */

-    public static Bitmap resizeToSquareBitmap(Bitmap inputBitmap, int size,

-            int backgroundColor) {

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "resizeToSquareBitmap: Input: " + inputBitmap.getWidth()

-                    + "x" + inputBitmap.getHeight() + ", output:" + size + "x" + size);

-        }

-

-        final Bitmap bitmap;

-        try {

-            // Create the new bitmap

-            bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);

-        } catch (OutOfMemoryError e) {

-            Log.w(TAG, "resizeToSquareBitmap OutOfMemoryError for image size: " + size, e);

-            return null;

-        }

-

-        if (bitmap == null) {

-            return null;

-        }

-

-        final Canvas canvas = new Canvas(bitmap);

-        if (backgroundColor != NO_COLOR) {

-            canvas.drawColor(backgroundColor);

-        }

-

-        if (inputBitmap.getWidth() != size || inputBitmap.getHeight() != size) {

-            final Rect src = new Rect(0, 0, inputBitmap.getWidth(), inputBitmap.getHeight());

-            final Rect dest = new Rect(0, 0, size, size);

-            synchronized(sResizePaint) {

-                canvas.drawBitmap(inputBitmap, src, dest, sResizePaint);

-            }

-        } else {

-            canvas.drawBitmap(inputBitmap, 0, 0, null);

-        }

-

-        return bitmap;

-    }

-

-    /**

-     * Resize and crop a bitmap.

-     *

-     * @param inputBitmap The input bitmap

-     * @param height The height

-     * @param width The width

-     *

-     * @return The new bitmap

-     */

-    public static Bitmap resizeAndCropBitmap(Bitmap inputBitmap, int width, int height) {

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "resizeAndCropBitmap: Input: " + inputBitmap.getWidth()

-                    + "x" + inputBitmap.getHeight() + ", output:"

-                    + width + "x" + height);

-        }

-

-        // Create the new bitmap

-        final Bitmap bitmap = Bitmap.createBitmap(

-                width, height, Bitmap.Config.ARGB_8888);

-        if (bitmap == null) {

-            return null;

-        }

-

-        final Canvas canvas = new Canvas(bitmap);

-        if (inputBitmap.getWidth() != width || inputBitmap.getHeight() != height) {

-            // create a source rectangle of the same aspect ratio as the requested size.

-            int croppedWidth = inputBitmap.getWidth();

-            int croppedHeight = inputBitmap.getHeight();

-            if (inputBitmap.getWidth() * height > inputBitmap.getHeight() * width) {

-                // the input bitmap is a wider aspect ratio.  Crop the sides.

-                croppedWidth = inputBitmap.getHeight() * width / height;

-            } else {

-                // The input bitmap is a taller aspect ratio.  Crop the top and bottom.

-                croppedHeight = inputBitmap.getWidth() * height / width;

-            }

-

-            int left = (inputBitmap.getWidth() - croppedWidth) / 2;

-            int top = (inputBitmap.getHeight() - croppedHeight) / 2;

-            final Rect src = new Rect(left, top,

-                    left + croppedWidth, top + croppedHeight);

-            final Rect dest = new Rect(0, 0, width, height);

-            synchronized(sResizePaint) {

-                canvas.drawBitmap(inputBitmap, src, dest, sResizePaint);

-            }

-        } else {

-            canvas.drawBitmap(inputBitmap, 0, 0, null);

-        }

-

-        return bitmap;

-    }

-

-    /**

-     * Resize a bitmap

-     *

-     * @param imageBytes The image bytes

-     * @param width The width of the resized image

-     * @param height The width of the resized image

-     *

-     * @return The resized bitmap

-     */

-    public static Bitmap resizeBitmap(byte[] imageBytes, int width, int height) {

-        final BitmapFactory.Options dbo = new BitmapFactory.Options();

-        dbo.inJustDecodeBounds = true;

-        decodeByteArray(imageBytes, 0, imageBytes.length, dbo);

-

-        final int nativeWidth = dbo.outWidth;

-        final int nativeHeight = dbo.outHeight;

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "resizeBitmap: Input: " + nativeWidth + "x" + nativeHeight

-                    + ", resize to: " + width + "x" + height);

-        }

-

-        final Bitmap srcBitmap;

-        if (nativeWidth > width || nativeHeight > height) {

-            final float bitmapWidth = (nativeWidth * width) / nativeHeight;

-            final float bitmapHeight = (nativeHeight * height) / nativeWidth;

-

-            if (nativeWidth / bitmapWidth > 1 || nativeHeight / bitmapHeight > 1) {

-                // Create a scaled bitmap

-                final BitmapFactory.Options options = new BitmapFactory.Options();

-                options.inSampleSize = Math.max(nativeWidth / (int)bitmapWidth,

-                        nativeHeight / (int)bitmapHeight);

-                srcBitmap = decodeByteArray(imageBytes, 0, imageBytes.length, options);

-            } else {

-                srcBitmap = decodeByteArray(imageBytes, 0, imageBytes.length);

-            }

-        } else {

-            srcBitmap = decodeByteArray(imageBytes, 0, imageBytes.length);

-        }

-

-        if (srcBitmap == null) {

-            return null;

-        }

-

-        // Crop the bitmap

-        final Bitmap croppedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

-        if (croppedBitmap == null) {

-            srcBitmap.recycle();

-            return null;

-        }

-

-        final int srcWidth = srcBitmap.getWidth();

-        final int srcHeight = srcBitmap.getHeight();

-

-        int croppedWidth = srcWidth;

-        int croppedHeight = srcHeight;

-        if (nativeWidth * height > width * nativeHeight) {

-            //  the input bitmap is a wider aspect ratio. Crop the sides.

-            croppedWidth = srcBitmap.getHeight() * width / height;

-        } else {

-            // the input bitmap is a taller aspect ratio.  Crop the top and bottom.

-            croppedHeight = srcBitmap.getWidth() * height / width;

-        }

-

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "resizeBitmap: cropped: " + croppedWidth + "x" + croppedHeight);

-        }

-

-        final int srcLeft = (srcWidth - croppedWidth) / 2;

-        final int srcTop = (srcHeight - croppedHeight) / 2;

-        final Rect src = new Rect(srcLeft, srcTop, srcLeft + croppedWidth, srcTop + croppedHeight);

-        final Rect dest = new Rect(0, 0, width, height);

-

-        final Canvas croppedCanvas = new Canvas(croppedBitmap);

-        croppedCanvas.drawColor(0xffe0e0e0);

-        synchronized (sResizePaint) {

-            croppedCanvas.drawBitmap(srcBitmap, src, dest, sResizePaint);

-        }

-

-        srcBitmap.recycle();

-

-        return croppedBitmap;

-    }

-

-    /**

-     * Resize the bitmap so that its height does not exceed the supplied value.

-     *

-     * @param imageBytes The image bytes

-     * @param height The maximum height of the scaled image

-     *

-     * @return The resized bitmap as bytes

-     */

-    public static byte[] resizeBitmapToHeight(byte[] imageBytes, int height) {

-        if (imageBytes == null) {

-            return imageBytes;

-        }

-

-        final BitmapFactory.Options dbo = new BitmapFactory.Options();

-        dbo.inJustDecodeBounds = true;

-        decodeByteArray(imageBytes, 0, imageBytes.length, dbo);

-

-        int nativeWidth = dbo.outWidth;

-        int nativeHeight = dbo.outHeight;

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "scaleBitmap: Input: " + nativeWidth + "x" + nativeHeight

-                    + ", resize to: " + height);

-        }

-

-        if (nativeHeight <= height) {

-            return imageBytes;

-        }

-

-        int width = (int) ((float) nativeWidth / nativeHeight * height);

-        Bitmap bitmap;

-        if (nativeWidth / width > 1 || nativeHeight / height > 1) {

-            BitmapFactory.Options options = new BitmapFactory.Options();

-            options.inSampleSize = Math.max(nativeWidth / width, nativeHeight / height);

-            bitmap = decodeByteArray(imageBytes, 0, imageBytes.length, options);

-            if (bitmap == null) {

-                return null;

-            }

-            nativeWidth = bitmap.getWidth();

-            nativeHeight = bitmap.getHeight();

-        } else {

-            bitmap = decodeByteArray(imageBytes, 0, imageBytes.length);

-            if (bitmap == null) {

-                return null;

-            }

-        }

-

-        Bitmap scaledBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

-        if (scaledBitmap == null) {

-            bitmap.recycle();

-            return null;

-        }

-

-        final Canvas canvas = new Canvas(scaledBitmap);

-        synchronized (sResizePaint) {

-            canvas.drawBitmap(bitmap, new Rect(0, 0, nativeWidth, nativeHeight),

-                    new Rect(0, 0, width, height), sResizePaint);

-        }

-        bitmap.recycle();

-        bitmap = null;

-

-        ByteArrayOutputStream stream = new ByteArrayOutputStream();

-        scaledBitmap.compress(CompressFormat.PNG, 100, stream);

-        scaledBitmap.recycle();

-        scaledBitmap = null;

-

-        return stream.toByteArray();

-    }

-

-    /**

-     * @param context The context

-     * @return A {@link ProgressDialog} informing the user a photo is being

-     *         inserted

-     */

-    public static Dialog createInsertCameraPhotoDialog(Context context) {

-        final ProgressDialog dialog = new ProgressDialog(context);

-        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

-        dialog.setCancelable(false);

-        dialog.setMessage(context.getString(R.string.dialog_inserting_camera_photo));

-        return dialog;

-    }

-

-    /**

-     * Inserts a newly taken photo into the media store. We cannot directly use

-     * {@code Images.Media#insertImage(ContentResolver, String, String, String)}

-     * as this method will not properly set the photo's timestamp. Additionally,

-     * any EXIF information in the original image is lost and there's a much higher

-     * chance for an OOME as insertImage() actually decodes the JPEG just to

-     * immediately re-encode it back to a JPEG.

-     * <p>

-     * NOTE: This code was shamelessly copied and merged from the Camera app

-     * [see method addImage() in Storage.java] and Images.Media#insertImage().

-     *

-     * NOTE: This method should not be called from the UI thread. It performs

-     * file IO and generates a thumbnail.

-     *

-     * @param context The context

-     * @param filename The name of the photo

-     * @return The media URL of the photo

-     * @throws FileNotFoundException If the file is not found

-     */

-    public static String insertCameraPhoto(Context context, String filename)

-            throws FileNotFoundException {

-        final File f = new File(Environment.getExternalStorageDirectory(), filename);

-

-        final long dateTaken = System.currentTimeMillis();

-        final String photoName = createPhotoName(context, dateTaken);

-        final ContentResolver resolver = context.getContentResolver();

-

-        // Insert into MediaStore

-        final ContentValues values = new ContentValues(5);

-        final int orientation = ImageUtils.getExifRotation(resolver, f.getAbsolutePath());

-

-        values.put(ImageColumns.TITLE, photoName);

-        values.put(ImageColumns.DISPLAY_NAME, photoName + ".jpg");

-        values.put(ImageColumns.DATE_TAKEN, dateTaken);

-        values.put(ImageColumns.MIME_TYPE, "image/jpeg");

-        values.put(ImageColumns.ORIENTATION, orientation);

-

-        // TODO(kkiyohara): be smarter about figuring out what storage is available, or

-        // maybe preventing the photo from being taken if the SD card (external storage)

-        // is missing.

-        Uri mediaUri;

-        try {

-            mediaUri = resolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values);

-        } catch (Exception e1) {

-            // here when saving to external failed, try internal

-            try {

-                mediaUri = resolver.insert(Images.Media.INTERNAL_CONTENT_URI, values);

-            } catch (Exception e2) {

-                try {

-                    // last chance, try save to HTC-specific PhoneStorage

-                    mediaUri = resolver.insert(MediaStoreUtils.PHONE_STORAGE_IMAGES_URI, values);

-                } catch (Exception e3) {

-                    Log.e(TAG, "Failed to save image", e3);

-                    return null;

-                }

-            }

-        }

-

-        try {

-            // On some platforms this method may throw a NullPointerException

-            final OutputStream imageOut = resolver.openOutputStream(mediaUri);

-            final FileInputStream imageIn = new FileInputStream(f);

-

-            try {

-                final int downloadBufferSize = 10240;

-                final byte[] array = new byte[downloadBufferSize];

-                int bytesRead;

-

-                do {

-                    bytesRead = imageIn.read(array);

-                    if (bytesRead == -1) {

-                        break;

-                    }

-                    imageOut.write(array, 0, bytesRead);

-                } while (true);

-            } finally {

-                imageOut.close();

-            }

-

-            // Wait until MINI_KIND thumbnail is generated.

-            //

-            // If Images.Media.EXTERNAL_CONTENT_URI is not writable, then

-            // it is not possible to generate the thumbnail using public APIs.

-            if (MediaStoreUtils.isExternalMediaStoreUri(mediaUri)) {

-                Bitmap bmp = MediaStoreUtils.getThumbnail(

-                        context, mediaUri, Images.Thumbnails.MINI_KIND);

-                bmp.recycle();

-                bmp = null;

-            }

-        } catch (FileNotFoundException fe) {

-            Log.e(TAG, "File not found", fe);

-            throw fe;

-        } catch (Exception e) {

-            Log.e(TAG, "Failed to insert image", e);

-            if (mediaUri != null) {

-                resolver.delete(mediaUri, null, null);

-                mediaUri = null;

-            }

-        } finally {

-            f.delete();

-        }

-

-        return (mediaUri == null ? null : mediaUri.toString());

-    }

-

-    /**

-     * Returns a a name that is consistent with the Android camera application.

-     */

-    private static String createPhotoName(Context context, long dateTaken) {

-        final Date date = new Date(dateTaken);

-        final SimpleDateFormat dateFormat =

-                new SimpleDateFormat(context.getString(R.string.image_file_name_format));

-

-        return dateFormat.format(date);

-    }

-

-    /**

-     * Gets a URL that can be used to download an image at the given size. The size specifies

-     * the maximum width or height of the image. If the given URL is either a FIFE URL or an

-     * Image Proxy URL, it will be modified to contain the proper sizing parameters. Otherwise,

-     * the URL will be converted to an Image Proxy URL.

-     *

-     * @return A URL that can be used to retrieve an image of the given size.

-     */

-    public static String getResizedUrl(int size, String url) {

-        if (FIFEUtil.isFifeHostedUrl(url)) {

-            return FIFEUtil.setImageUrlSize(size, url, false);

-        } else {

-            return ImageProxyUtil.setImageUrlSize(size, url);

-        }

-    }

-

-    /**

-     * Gets a URL that can be used to download an image at the given size. The size specifies

-     * the maximum width or height of the image. If the given URL is either a FIFE URL or an

-     * Image Proxy URL, it will be modified to contain the proper sizing parameters. Otherwise,

-     * the URL will be converted to an Image Proxy URL.

-     *

-     * @return A URL that can be used to retrieve an image of the given size.

-     */

-    public static String getResizedUrl(int width, int height, String url) {

-        if (FIFEUtil.isFifeHostedUrl(url)) {

-            return FIFEUtil.setImageUrlSize(width, height, url, false, false);

-        } else {

-            return ImageProxyUtil.setImageUrlSize(width, height, url);

-        }

-    }

-

-    /**

-     * See {@link #getCroppedAndResizedUrl(int, String)} for more information. This method

-     * differs from getCroppedAndResizedUrl because it attempts to get a center cropped

-     * version of the requested image. This is only possible for FIFE hosted URLs; Image

-     * Proxy URLs will work as they do in getCroppedAndResizedUrl.

-     *

-     * @return A URL that can be used to retrieve an image of the given size.

-     */

-    public static String getCenterCroppedAndResizedUrl(int width, int height, String url) {

-        if (url == null) {

-            return null;

-        }

-

-        if (FIFEUtil.isFifeHostedUrl(url)) {

-            final StringBuilder options = new StringBuilder();

-            options.append("w").append(width);

-            options.append("-h").append(height);

-            options.append("-d");

-            options.append("-n");

-            return FIFEUtil.setImageUrlOptions(options.toString(), url).toString();

-        } else {

-            return ImageProxyUtil.setImageUrlSize(width, height, url);

-        }

-    }

-

-    /**

-     * See {@link #getResizedUrl(int, String)} for more information. This method differs

-     * from getResizedUrl because it attempts to get a cropped version of the requested

-     * image, meaning that for a given size, the returned image will be of dimension size

-     * in both x and y. This is only possible for FIFE hosted URLs; Image Proxy URLs will

-     * work as they do in getResizedUrl.

-     *

-     * @param size The size

-     * @param url The URL

-     * @return A URL that can be used to retrieve an image of the given size,

-     *         cropped if possible.

-     */

-    public static String getCroppedAndResizedUrl(int size, String url) {

-        if (FIFEUtil.isFifeHostedUrl(url)) {

-            return FIFEUtil.setImageUrlSize(size, url, true);

-        } else {

-            // The image proxy has no facility to crop images

-            return ImageProxyUtil.setImageUrlSize(size, url);

-        }

-    }

-

-    /**

-     * For some images, namely PNG images, the decode ignores the preferred config option and

-     * always decodes them as 32bpp. On devices that will see the most benefit, we re-encode

-     * the image as 16bpp. Otherwise, prefer to have greater fidelity in a PNG. The specified

-     * bitmap will be recycled automatically as necessary.

-     */

-    public static Bitmap getLowResBitmap(Bitmap bitmap) {

-        if (bitmap == null) {

-            return null;

-        }

-

-        if (bitmap.getConfig() == Config.ARGB_8888) {

-            final int width = bitmap.getWidth();

-            final int height = bitmap.getHeight();

-            final Bitmap lowResBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

-            final Canvas canvas = new Canvas(lowResBitmap);

-            final Rect src = new Rect(0, 0, width, height);

-            final Rect dest = new Rect(0, 0, width, height);

-

-            synchronized(sResizePaint) {

-                canvas.drawBitmap(bitmap, src, dest, sResizePaint);

-            }

-            bitmap.recycle();

-            return lowResBitmap;

-        }

-        return bitmap;

     }

 

     /**

@@ -1170,208 +170,4 @@
             }

         }

     }

-

-    /**

-     * Get the file path of a media item

-     *

-     * @return the filepath for a given MediaStore uri, or null if there was a

-     *         problem

-     */

-    private static String getFilePath(ContentResolver resolver, Uri uri) {

-        // Ask MediaStore for the actual file path

-        final Cursor cursor = resolver.query(uri,

-                new String[] {MediaColumns._ID, MediaColumns.DATA}, null, null, null);

-        if (cursor == null) {

-            Log.w(TAG, "getFilePath: query returned null cursor for uri=" + uri);

-            return null;

-        }

-

-        String path = null;

-        try {

-            if (!cursor.moveToFirst()) {

-                Log.w(TAG, "getFilePath: query returned empty cursor for uri=" + uri);

-                return null;

-            }

-

-            // Get the file path

-            path = cursor.getString(cursor.getColumnIndexOrThrow(MediaColumns.DATA));

-            if (TextUtils.isEmpty(path)) {

-                Log.w(TAG, "getFilePath: MediaColumns.DATA was empty for uri=" + uri);

-                return null;

-            }

-        } finally {

-            cursor.close();

-        }

-

-        return path;

-    }

-

-    /**

-     * Encode the given image as a Base64 string (recycle the bitmap)

-     *

-     * @param imageBytes The image bytes

-     *

-     * @return A base64 encoded string

-     */

-    public static String encodeImageBytes(byte[] imageBytes) {

-        String base64 = Base64.encodeToString(imageBytes, Base64.NO_WRAP);

-        return "data:image/jpeg;base64," + base64;

-    }

-

-    /**

-     * Decode an image from a Base64 string

-     *

-     * @param string A base64 encoded string

-     *

-     * @return The image bytes

-     */

-    public static byte[] decodeImageBytes(String string) {

-        int start = string.indexOf("base64,");

-        if (start == -1) {

-            return null;

-        }

-

-        return Base64.decode(string.substring(start+7), Base64.DEFAULT);

-    }

-

-    /**

-     * Compress the bitmap to JPEG and return the compressed image bytes. The given bitmap will

-     * be recycled.

-     *

-     * @param bitmap The bitmap

-     * @param quality the quality level for JPEG coding (90 is default).

-     *

-     * @return The compressed image bytes

-     */

-    public static byte[] compressBitmap(Bitmap bitmap, int quality) {

-        final ByteArrayOutputStream stream = new ByteArrayOutputStream();

-        try {

-            bitmap.compress(CompressFormat.JPEG, quality, stream); // Copy #1

-            stream.flush();

-        } catch (IOException ignore) {

-        } finally {

-            try {

-                stream.close();

-            } catch (IOException ignore) {

-            }

-        }

-        bitmap.recycle();

-        bitmap = null;

-

-        final byte[] imageBytes = stream.toByteArray(); // Copy #2

-        if (Log.isLoggable(TAG, Log.DEBUG)) {

-            Log.d(TAG, "compressBitmap: Image size: " + imageBytes.length);

-        }

-        return imageBytes;

-    }

-

-    /**

-     * Compress the bitmap to JPEG and return the compressed image bytes. The given bitmap will

-     * be recycled.  A default quality level of 90 is used.

-     *

-     * @param bitmap The bitmap

-     *

-     * @return The compressed image bytes

-     */

-    public static byte[] compressBitmap(Bitmap bitmap) {

-        return compressBitmap(bitmap, DEFAULT_JPEG_QUALITY);

-    }

-

-    /**

-     * Retrieve the EXIF rotation of an image

-     *

-     * @param cr the content resolver, only used when the path given is an

-     *      actual content uri.

-     * @param path an absolute file path to the photo for which we want to get

-     *      the rotation angle. Can also be a content uri, in which case

-     *      the content resolver is used.

-     *

-     * @return the number of degrees an image needs to be rotated to face the

-     *      "correct" way. Does this by reading the actual file's EXIF

-     *       metadata.

-     */

-    private static int getExifRotation(ContentResolver cr, String path) {

-        // create the Exif interface

-        ExifInterface exif = null;

-        try {

-            exif = new ExifInterface(path);

-        } catch (IOException e) {

-            Log.w(TAG, "failed to create ExifInterface for " + path);

-        }

-

-        if (exif == null) {

-            return 0;

-        }

-

-        // get and translate the orientation

-        int orientation = exif.getAttributeInt(

-                ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

-

-        int degrees = 0;

-        switch (orientation) {

-            case ExifInterface.ORIENTATION_NORMAL:

-                degrees = 0;

-                break;

-

-            case ExifInterface.ORIENTATION_ROTATE_90:

-                degrees = 90;

-                break;

-

-            case ExifInterface.ORIENTATION_ROTATE_180:

-                degrees = 180;

-                break;

-

-            case ExifInterface.ORIENTATION_ROTATE_270:

-                degrees = 270;

-                break;

-        }

-

-        return degrees;

-    }

-

-    /**

-     * Rotate a bitmap based on the MediaStore uri's EXIF information.

-     *

-     * @param cr standard content resolver

-     * @param uri MediaStore uri

-     * @param bmp bitmap to rotated

-     * @return bitmap with proper orientation

-     */

-    public static Bitmap rotateBitmap(ContentResolver cr, Uri uri, Bitmap bmp) {

-        if (bmp != null) {

-            final String path = getFilePath(cr, uri);

-            final int degrees = getExifRotation(cr, path);

-            if (degrees != 0) {

-                bmp = rotateBitmap(bmp, degrees);

-            }

-        }

-        return bmp;

-    }

-

-    /**

-     * Bitmap rotation method

-     *

-     * @param bitmap The input bitmap

-     * @param degrees The rotation angle

-     */

-    private static Bitmap rotateBitmap(Bitmap bitmap, int degrees) {

-        if (degrees != 0 && bitmap != null) {

-            final Matrix m = new Matrix();

-            final int w = bitmap.getWidth();

-            final int h = bitmap.getHeight();

-            m.setRotate(degrees, (float) w / 2, (float) h / 2);

-

-            try {

-                final Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, m, true);

-                if (bitmap != rotatedBitmap) {

-                    bitmap.recycle();

-                    bitmap = rotatedBitmap;

-                }

-            } catch (OutOfMemoryError ex) {

-                // We have no memory to rotate. Return the original bitmap.

-            }

-        }

-

-        return bitmap;

-    }

 }

diff --git a/src/com/android/mail/photo/util/MediaStoreUtils.java b/src/com/android/mail/photo/util/MediaStoreUtils.java
deleted file mode 100644
index e1650e8..0000000
--- a/src/com/android/mail/photo/util/MediaStoreUtils.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.photo.util;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.provider.MediaStore;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utilities for MediaStore.
- */
-public class MediaStoreUtils {
-    public static final String TAG = "MediaStoreUtils";
-
-    // Special HTC-only MediaStore storage volume
-    public static final Uri PHONE_STORAGE_IMAGES_URI =
-            MediaStore.Images.Media.getContentUri("phoneStorage");
-
-    public static final Uri PHONE_STORAGE_VIDEO_URI =
-            MediaStore.Video.Media.getContentUri("phoneStorage");
-
-    /**
-     * Define constants for Video info query.
-     */
-    @SuppressWarnings("unused")
-    private static interface VideoQuery {
-        /** Projection of the VideoQuery cursors */
-        public static final String[] PROJECTION = {
-            BaseColumns._ID,
-            MediaStore.Video.VideoColumns.DURATION,
-            MediaStore.Video.VideoColumns.RESOLUTION,
-        };
-
-        public static final int INDEX_ID = 0;
-        public static final int INDEX_DURATION_MSEC = 1;
-        public static final int INDEX_RESOLUTION = 2;
-    }
-
-    /** regex used to parse video resolution "XxY" -- never trust MediaStore! */
-    private static final Pattern PAT_RESOLUTION = Pattern.compile("(\\d+)[xX](\\d+)");
-
-    /**
-     * Prevent instantiation
-     */
-    private MediaStoreUtils() {
-    }
-
-    /**
-     * Check if a URI is from the MediaStore
-     *
-     * @param uri The URI
-     */
-    public static boolean isMediaStoreUri(Uri uri) {
-        return uri != null
-                && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
-                && MediaStore.AUTHORITY.equals(uri.getAuthority());
-    }
-
-    /**
-     * Checks if a {@link Uri} is an external {@link MediaStore} URI.
-     * <p>
-     * The {@code getThumbnail} methods of {@link MediaStore} are hard-coded to
-     * only support external media URIs. There is an API for loading internal
-     * thumbnails, but it is not public and the code cannot be copied easily.
-     *
-     * @param uri a content URI.
-     * @return {@code true} if the {@link Uri} belongs to {@link MediaStore} and
-     *         is external, {@code false} otherwise.
-     * @throws NullPointerException if the argument is {@code null}.
-     * @see android.provider.MediaStore.Images.Media#EXTERNAL_CONTENT_URI
-     * @see android.provider.MediaStore.Video.Media#EXTERNAL_CONTENT_URI
-     */
-    public static boolean isExternalMediaStoreUri(Uri uri) {
-        if (isMediaStoreUri(uri)) {
-            String path = uri.getPath();
-            String externalImagePrefix = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.getPath();
-            String externalVideoPrefix = MediaStore.Video.Media.EXTERNAL_CONTENT_URI.getPath();
-            return path.startsWith(externalImagePrefix) || path.startsWith(externalVideoPrefix);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * @return true if the MimeType type is image
-     */
-    public static boolean isImageMimeType(String mimeType) {
-        return mimeType != null && mimeType.startsWith("image/");
-    }
-
-    /**
-     * @return true if the MimeType type is video
-     */
-    public static boolean isVideoMimeType(String mimeType) {
-        return mimeType != null && mimeType.startsWith("video/");
-    }
-
-    /**
-     * Gets the MediaStore thumbnail bitmap for an image or video.
-     *
-     * @param context this can be an Application Context
-     * @param uri image or video Uri
-     * @param kind MediaStore.{Images|Video}.Thumbnails.MINI_KIND or MICRO_KIND
-     * @return thumbnail bitmap or null
-     */
-    public static Bitmap getThumbnail(Context context, Uri uri, int kind) {
-        // determine actual pixel dimensions
-        final int microSize = ImageUtils.getMaxThumbnailDimension(context, kind);
-        return getThumbnailHelper(context, uri, microSize, microSize, kind);
-    }
-
-    /**
-     * Gets the MediaStore thumbnail bitmap for an image or video.
-     *
-     * @param context this can be an Application Context
-     * @param uri image or video Uri
-     * @param width desired output width
-     * @param height desired output height
-     * @return thumbnail bitmap or null
-     */
-    public static Bitmap getThumbnail(Context context, Uri uri, int width, int height) {
-        // determine if we want mini or micro thumbnails
-        final int microSize = ImageUtils.getMaxThumbnailDimension(context,
-                MediaStore.Images.Thumbnails.MICRO_KIND);
-        int kind = (width > microSize || height > microSize)
-                ? MediaStore.Images.Thumbnails.MINI_KIND
-                : MediaStore.Images.Thumbnails.MICRO_KIND;
-
-        return getThumbnailHelper(context, uri, width, height, kind);
-    }
-
-    /**
-     * Deletes the MediaStore entry and, as necessary on some pre-ICS devices, corresponding
-     * native file
-     *
-     * @param resolver context reolver
-     * @param localContentUri image or video Uri
-     * @return true if delete succeeds, false otherwise
-     */
-    public static boolean deleteLocalFileAndMediaStore(ContentResolver resolver,
-            Uri localContentUri) {
-        final String filePath = MediaStoreUtils.getFilePath(resolver, localContentUri);
-
-        boolean status = resolver.delete(localContentUri, null, null) == 1;
-
-        if (status && filePath != null) {
-            final File file = new File(filePath);
-            if (file.exists()) {
-                status = file.delete();
-            }
-        }
-
-        return status;
-    }
-
-    /**
-     * Safe method to retrieve mimetype of a content Uri.
-     *
-     * On some phones, getType() can throw an exception for no good reason.
-     *
-     * @param resolver is a standard ContentResolver
-     * @param uri is a the target content Uri
-     * @return valid mime-type; null if type was unknown or an exception was thrown
-     */
-    public static String safeGetMimeType(ContentResolver resolver, Uri uri) {
-        String mimeType = null;
-        try {
-            mimeType = resolver.getType(uri);
-        } catch (Exception e) {
-            if (Log.isLoggable(TAG, Log.WARN)) {
-                Log.w(TAG, "safeGetMimeType failed for uri=" + uri, e);
-            }
-        }
-        return mimeType;
-    }
-
-//    /**
-//     * Converts a MediaStore video Uri to VideoData proto byte array.
-//     *
-//     * @param context can be an ApplicationContext
-//     * @param uri is a MediaStore Video Uri
-//     * @return byte[] proto byte array, or null if Uri is not a MediaStore video
-//     */
-//    public static byte[] toVideoDataBytes(Context context, Uri uri) {
-//        final VideoData videoData = toVideoData(context, uri);
-//        return videoData == null ? null : videoData.toByteArray();
-//    }
-//
-//    /**
-//     * Converts a MediaStore video Uri to an array of VideoData proto.
-//     *
-//     * @param context can be an ApplicationContext
-//     * @param uri is a MediaStore Video Uri
-//     * @return VideoData proto byte array, or null if Uri is not a MediaStore video
-//     */
-//    public static VideoData toVideoData(Context context, Uri uri) {
-//        // see if this is a video
-//        final ContentResolver cr = context.getContentResolver();
-//        if (!MediaStoreUtils.isVideoMimeType(safeGetMimeType(cr, uri))) {
-//            return null;
-//        }
-//
-//        // format VideoStream info
-//        final VideoStream.Builder vs = VideoStream.newBuilder();
-//        vs.setStreamUrl(uri.toString());
-//
-//        // 0 == unknown format, see ContentHeader.VideoFormat.INVALID_VIDEO_FORMAT
-//        vs.setFormatId(0);
-//
-//        // query for resolution -- string formatted as "XxY"
-//        int width = 0;
-//        int height = 0;
-//        long durationMsec = 0L;
-//        final Cursor cursor = cr.query(uri, VideoQuery.PROJECTION, null, null, null);
-//        if (cursor != null) {
-//            try {
-//                if (cursor.moveToFirst()) {
-//                    durationMsec = cursor.getLong(VideoQuery.INDEX_DURATION_MSEC);
-//
-//                    final String resolution = cursor.getString(VideoQuery.INDEX_RESOLUTION);
-//                    if (resolution != null) {
-//                        final Matcher m = PAT_RESOLUTION.matcher(resolution);
-//                        if (m.find()) {
-//                            width = Integer.parseInt(m.group(1));
-//                            height = Integer.parseInt(m.group(2));
-//                        }
-//                    }
-//                }
-//            } finally {
-//                cursor.close();
-//            }
-//        }
-//        vs.setVideoWidth(width);
-//        vs.setVideoHeight(height);
-//
-//        // manufacture VideoData bytes
-//        final VideoData vd = VideoData.newBuilder()
-//                .setStatus(VideoStatus.FINAL)
-//                .setDuration(durationMsec)
-//                .addStream(vs)
-//                .build();
-//        return vd;
-//    }
-
-    /**
-     * @return the file path for a given MediaStore uri, or null if there was a problem
-     */
-    private static String getFilePath(ContentResolver cr, Uri uri) {
-        // ask MediaStore for the actual file path
-        Cursor cursor = cr.query(uri, new String [] {MediaStore.MediaColumns.DATA},
-                null, null, null);
-        if (cursor == null) {
-            if (Log.isLoggable(TAG, Log.WARN)) {
-                Log.w(TAG, "getFilePath: query returned null cursor for uri=" + uri);
-            }
-            return null;
-        }
-
-        String path = null;
-        try {
-            if (!cursor.moveToFirst()) {
-                if (Log.isLoggable(TAG, Log.WARN)) {
-                    Log.w(TAG, "getFilePath: query returned empty cursor for uri=" + uri);
-                }
-                return null;
-            }
-            // read the file path
-            path = cursor.getString(0);
-            if (TextUtils.isEmpty(path)) {
-                if (Log.isLoggable(TAG, Log.WARN)) {
-                    Log.w(TAG, "getFilePath: MediaColumns.DATA was empty for uri=" + uri);
-                }
-                return null;
-            }
-
-        } finally {
-            cursor.close();
-        }
-        return path;
-    }
-
-    /**
-     * Gets the MediaStore thumbnail bitmap for an image or video.
-     *
-     * @param context this can be an Application Context
-     * @param uri image or video URI
-     * @param width desired output width
-     * @param height desired output height
-     * @param kind MediaStore.{Images|Video}.Thumbnails.MINI_KIND or MICRO_KIND
-     * @return the thumb nail image, or {@code null}
-     */
-    private static Bitmap getThumbnailHelper(
-            Context context, Uri uri, int width, int height, int kind) {
-        // guard against bogus Uri's
-        if (uri == null) {
-            return null;
-        }
-
-        // Thumb nails are only available for external media URIs
-        if (!isExternalMediaStoreUri(uri)) {
-            return null;
-        }
-
-        final ContentResolver cr = context.getContentResolver();
-        final long id = ContentUris.parseId(uri);
-
-        // query the appropriate MediaStore thumb nail provider
-        final String mimeType = safeGetMimeType(cr, uri);
-        Bitmap bmp;
-        if (isImageMimeType(mimeType)) {
-            bmp = MediaStore.Images.Thumbnails.getThumbnail(cr, id, kind, null);
-
-        } else if (isVideoMimeType(mimeType)) {
-            bmp = MediaStore.Video.Thumbnails.getThumbnail(cr, id, kind, null);
-
-        } else {
-            if (Log.isLoggable(TAG, Log.WARN)) {
-                Log.w(TAG, "getThumbnail: unrecognized mimeType=" + mimeType + ", uri=" + uri);
-            }
-            return null;
-        }
-
-        // if we got the thumb nail, we still have to rotate and crop as necessary
-        if (bmp != null) {
-            bmp = ImageUtils.rotateBitmap(cr, uri, bmp);
-
-            if (bmp.getWidth() != width || bmp.getHeight() != height) {
-                final Bitmap resizedBitmap = ImageUtils.resizeAndCropBitmap(
-                        bmp, width, height);
-                bmp.recycle();
-                bmp = resizedBitmap;
-            }
-        }
-        return bmp;
-    }
-}
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index ef3dc6c..905d093 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -52,6 +52,10 @@
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.providers.UIProvider.EditSettingsExtras;
+import com.google.android.common.html.parser.HtmlDocument;
+import com.google.android.common.html.parser.HtmlParser;
+import com.google.android.common.html.parser.HtmlTree;
+import com.google.android.common.html.parser.HtmlTreeBuilder;
 import com.google.common.collect.Maps;
 
 import org.json.JSONObject;
@@ -508,6 +512,29 @@
     }
 
     /**
+     * Returns displayable text from the provided HTML string.
+     *
+     * @param htmlText HTML string
+     * @return Plain text string representation of the specified Html string
+     */
+    public static String convertHtmlToPlainText(String htmlText) {
+        return getHtmlTree(htmlText).getPlainText();
+    }
+
+    /**
+     * Returns a {@link HtmlTree} representation of the specified HTML string.
+     */
+    public static HtmlTree getHtmlTree(String htmlText) {
+        final HtmlParser parser = new HtmlParser();
+        final HtmlDocument doc = parser.parse(htmlText);
+        final HtmlTreeBuilder builder = new HtmlTreeBuilder();
+        doc.accept(builder);
+
+        return builder.getTree();
+    }
+
+
+    /**
      * Perform a simulated measure pass on the given child view, assuming the
      * child has a ViewGroup parent and that it should be laid out within that
      * parent with a matching width but variable height. Code largely lifted