1bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp/*
2bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * Copyright (C) 2011 Google Inc.
3bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * Licensed to The Android Open Source Project.
4bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp *
5bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * Licensed under the Apache License, Version 2.0 (the "License");
6bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * you may not use this file except in compliance with the License.
7bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * You may obtain a copy of the License at
8bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp *
9bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp *      http://www.apache.org/licenses/LICENSE-2.0
10bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp *
11bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * Unless required by applicable law or agreed to in writing, software
12bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * distributed under the License is distributed on an "AS IS" BASIS,
13bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * See the License for the specific language governing permissions and
15bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * limitations under the License.
16bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp */
17bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
18bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copppackage com.android.ex.photo.util;
19bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
20bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.content.ContentResolver;
21bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.graphics.Bitmap;
22bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.graphics.BitmapFactory;
239323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookimport android.graphics.Matrix;
24bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.graphics.Point;
25bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.graphics.Rect;
26bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.net.Uri;
27bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.os.Build;
289323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookimport android.util.DisplayMetrics;
29bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport android.util.Log;
30bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
31bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport com.android.ex.photo.PhotoViewActivity;
329323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookimport com.android.ex.photo.util.Exif;
33bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
34237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrookimport java.io.ByteArrayInputStream;
359323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookimport java.io.ByteArrayOutputStream;
36bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport java.io.FileNotFoundException;
37bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport java.io.IOException;
38bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport java.io.InputStream;
39bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport java.net.MalformedURLException;
40bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Coppimport java.net.URL;
41bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
429323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
43bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp/**
44bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp * Image utilities
45bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp */
46bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copppublic class ImageUtils {
47bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    // Logging
48bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    private static final String TAG = "ImageUtils";
49bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
50bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /** Minimum class memory class to use full-res photos */
51bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    private final static long MIN_NORMAL_CLASS = 32;
52bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /** Minimum class memory class to use small photos */
53bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    private final static long MIN_SMALL_CLASS = 24;
54bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
55bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public static enum ImageSize {
56bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        EXTRA_SMALL,
57bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        SMALL,
58bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        NORMAL,
59bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
60bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
61bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public static final ImageSize sUseImageSize;
62bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    static {
63bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        // On HC and beyond, assume devices are more capable
64bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        if (Build.VERSION.SDK_INT >= 11) {
65bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            sUseImageSize = ImageSize.NORMAL;
66bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } else {
67bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
68bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                // We have plenty of memory; use full sized photos
69bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                sUseImageSize = ImageSize.NORMAL;
70bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
71bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                // We have slight less memory; use smaller sized photos
72bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                sUseImageSize = ImageSize.SMALL;
73bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } else {
74bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                // We have little memory; use very small sized photos
75bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                sUseImageSize = ImageSize.EXTRA_SMALL;
76bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            }
77bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        }
78bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
79bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
80bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /**
81bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @return true if the MimeType type is image
82bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     */
83bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public static boolean isImageMimeType(String mimeType) {
84bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        return mimeType != null && mimeType.startsWith("image/");
85bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
86bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
87bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /**
88bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * Create a bitmap from a local URI
89bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *
90bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param resolver The ContentResolver
91bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param uri The local URI
92bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param maxSize The maximum size (either width or height)
93bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *
94f24e7099f9688518d705116c1b4279fc6279d3d6Mark Wei     * @return The new bitmap or null
95bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     */
96bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
97bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        // TODO: make this method not download the image for both getImageBounds and decodeStream
98bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        InputStream inputStream = null;
99bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        try {
100bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            final BitmapFactory.Options opts = new BitmapFactory.Options();
101bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            final Point bounds = getImageBounds(resolver, uri);
102bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            inputStream = openInputStream(resolver, uri);
1037aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp            if (bounds == null || inputStream == null) {
1047aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp                return null;
1057aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp            }
106bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
107bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
108bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
109bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
110bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            // Correct thumbnail orientation as necessary
111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            // TODO: Fix rotation if it's actually a problem
112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            //return rotateBitmap(resolver, uri, decodedBitmap);
113f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein            return decodedBitmap;
114bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
115bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } catch (FileNotFoundException exception) {
116bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            // Do nothing - the photo will appear to be missing
117bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } catch (IOException exception) {
118bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            // Do nothing - the photo will appear to be missing
1191f00d9d7c4beb007d491fc766f7a0c8fc4977a50The Android Open Source Project        } catch (IllegalArgumentException exception) {
1201f00d9d7c4beb007d491fc766f7a0c8fc4977a50The Android Open Source Project            // Do nothing - the photo will appear to be missing
121bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } finally {
122bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            try {
123bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                if (inputStream != null) {
124bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                    inputStream.close();
125bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                }
126bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } catch (IOException ignore) {
127bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            }
128bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        }
129bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        return null;
130bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
131bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
132bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /**
133bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
134bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * BitmapFactory.Options)} that returns {@code null} on {@link
135bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * OutOfMemoryError}.
136bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *
137bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param is The input stream that holds the raw data to be decoded into a
138bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *           bitmap.
139bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param outPadding If not null, return the padding rect for the bitmap if
140bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
141bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *                   no bitmap is returned (null) then padding is
142bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *                   unchanged.
143bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param opts null-ok; Options that control downsampling and whether the
144bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *             image should be completely decoded, or just is size returned.
145bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @return The decoded bitmap, or null if the image data could not be
146bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *         decoded, or, if opts is non-null, if opts requested only the
147bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *         size be returned (in opts.outWidth and opts.outHeight)
148bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     */
149bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
1509323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        ByteArrayOutputStream out = null;
151237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook        InputStream byteStream = null;
152bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        try {
1539323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            out = new ByteArrayOutputStream();
1549323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            final byte[] buffer = new byte[4096];
1559323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            int n = is.read(buffer);
1569323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            while (n >= 0) {
1579323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                out.write(buffer, 0, n);
1589323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                n = is.read(buffer);
1599323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
160237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook
1619323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            final byte[] bitmapBytes = out.toByteArray();
1629323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1639323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Determine the orientation for this image
1649323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            final int orientation = Exif.getOrientation(bitmapBytes);
165237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook
166237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook            // Create an InputStream from this byte array
167237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook            byteStream = new ByteArrayInputStream(bitmapBytes);
168237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook
169237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook            final Bitmap originalBitmap = BitmapFactory.decodeStream(byteStream, outPadding, opts);
1709323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1719323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (originalBitmap != null && orientation != 0) {
1729323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                final Matrix matrix = new Matrix();
1739323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                matrix.postRotate(orientation);
1749323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(),
1759323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                        originalBitmap.getHeight(), matrix, true);
1769323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1779323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            return originalBitmap;
178bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } catch (OutOfMemoryError oome) {
179bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
180bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            return null;
1819323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        } catch (IOException ioe) {
1829323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe);
1839323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            return null;
1849323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        } finally {
1859323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (out != null) {
1869323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                try {
1879323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    out.close();
1889323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                } catch (IOException e) {
1899323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    // Do nothing
1909323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                }
1919323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
192237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook            if (byteStream != null) {
193237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook                try {
194237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook                    byteStream.close();
195237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook                } catch (IOException e) {
196237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook                    // Do nothing
197237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook                }
198237ded1a59b1fc148a3ffcf78d6f9d34af5c170cPaul Westbrook            }
199bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        }
200bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
201bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
202bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    /**
203bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * Gets the image bounds
204bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *
205bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param resolver The ContentResolver
206bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @param uri The uri
207bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     *
208bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     * @return The image bounds
209bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp     */
210bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    private static Point getImageBounds(ContentResolver resolver, Uri uri)
211bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            throws IOException {
212bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        final BitmapFactory.Options opts = new BitmapFactory.Options();
213bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        InputStream inputStream = null;
214bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        String scheme = uri.getScheme();
215bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        try {
216bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            opts.inJustDecodeBounds = true;
217bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            inputStream = openInputStream(resolver, uri);
2187aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp            if (inputStream == null) {
2197aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp                return null;
2207aabd796bbcb149db9ac183c1d0aec09fccd42a4Adam Copp            }
221bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            decodeStream(inputStream, null, opts);
222bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
223bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            return new Point(opts.outWidth, opts.outHeight);
224bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        } finally {
225bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            try {
226bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                if (inputStream != null) {
227bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                    inputStream.close();
228bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                }
229bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } catch (IOException ignore) {
230bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            }
231bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        }
232bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
233bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp
234bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    private static InputStream openInputStream(ContentResolver resolver, Uri uri) throws
235bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            FileNotFoundException {
236bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        String scheme = uri.getScheme();
237bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        if("http".equals(scheme) || "https".equals(scheme)) {
238bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            try {
239bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                return new URL(uri.toString()).openStream();
240bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } catch (MalformedURLException e) {
241bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                // Fall-back to the previous behaviour, just in case
242bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                Log.w(TAG, "Could not convert the uri to url: " + uri.toString());
243bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                return resolver.openInputStream(uri);
244bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            } catch (IOException e) {
245bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                Log.w(TAG, "Could not open input stream for uri: " + uri.toString());
246bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp                return null;
247bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp            }
248bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        }
249bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp        return resolver.openInputStream(uri);
250bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp    }
251bd8b47b147f29f196f37d66ccb561d40414ab5b6Adam Copp}
252