OSDN Git Service

Make wallpaper picker/cropper more robust
authorMichael Jurka <mikejurka@google.com>
Tue, 29 Oct 2013 14:50:06 +0000 (15:50 +0100)
committerMichael Jurka <mikejurka@google.com>
Tue, 29 Oct 2013 21:50:30 +0000 (22:50 +0100)
- don't crash if image passed to wallpaper picker
is invalid
- close input streams correctly

Bug: 11413915
Bug: 11380658
Bug: 11362731

Change-Id: I973e6bdc532d24a64efd6d174e89fdac626d7ee3

res/values/strings.xml
src/com/android/launcher3/SavedWallpaperImages.java
src/com/android/launcher3/WallpaperCropActivity.java
src/com/android/launcher3/WallpaperPickerActivity.java
src/com/android/photos/BitmapRegionTileSource.java

index cafa424..1997c8b 100644 (file)
     <string name="folder_name"></string>
     <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
     <string name="wallpaper_instructions">Set wallpaper</string>
+    <!-- Error message when an image is selected as a wallpaper,
+         but the wallpaper picker cannot load it -->
+    <string name="image_load_fail">Coudn\'t load image</string>
+    <!-- Error message when an image is selected as a wallpaper,
+         but the wallpaper cropper cannot load it. The user will
+         usually see this when using another app and trying to set
+         an image as the wallpaper -->
+    <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
     <!-- Shown when wallpapers are selected in Wallpaper picker -->
     <!-- String indicating how many media item(s) is(are) selected
             eg. 1 selected [CHAR LIMIT=30] -->
index 086d085..58add70 100644 (file)
@@ -62,7 +62,7 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
             File file = new File(a.getFilesDir(), imageFilename);
             BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
                     new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
-            a.setCropViewTileSource(bitmapSource, false, true);
+            a.setCropViewTileSource(bitmapSource, false, true, null);
         }
         @Override
         public void onSave(WallpaperPickerActivity a) {
index 29e8c97..491316d 100644 (file)
@@ -41,10 +41,12 @@ import android.util.Log;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.Toast;
 
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.exif.ExifInterface;
 import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -109,12 +111,24 @@ public class WallpaperCropActivity extends Activity {
                 });
 
         // Load image in background
-        setCropViewTileSource(
-                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024), true, false);
+        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+        Runnable onLoad = new Runnable() {
+            public void run() {
+                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
+                    Toast.makeText(WallpaperCropActivity.this,
+                            getString(R.string.wallpaper_load_fail),
+                            Toast.LENGTH_LONG).show();
+                    finish();
+                }
+            }
+        };
+        setCropViewTileSource(bitmapSource, true, false, onLoad);
     }
 
-    public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
-            final boolean touchEnabled, final boolean moveToLeft) {
+    public void setCropViewTileSource(
+            final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
+            final boolean moveToLeft, final Runnable postExecute) {
         final Context context = WallpaperCropActivity.this;
         final View progressView = findViewById(R.id.loading);
         final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
@@ -127,13 +141,18 @@ public class WallpaperCropActivity extends Activity {
             protected void onPostExecute(Void arg) {
                 if (!isCancelled()) {
                     progressView.setVisibility(View.INVISIBLE);
-                    mCropView.setTileSource(
-                            new BitmapRegionTileSource(context, bitmapSource), null);
-                    mCropView.setTouchEnabled(touchEnabled);
-                    if (moveToLeft) {
-                        mCropView.moveToLeft();
+                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                        mCropView.setTileSource(
+                                new BitmapRegionTileSource(context, bitmapSource), null);
+                        mCropView.setTouchEnabled(touchEnabled);
+                        if (moveToLeft) {
+                            mCropView.moveToLeft();
+                        }
                     }
                 }
+                if (postExecute != null) {
+                    postExecute.run();
+                }
             }
         };
         // We don't want to show the spinner every time we load an image, because that would be
@@ -235,10 +254,12 @@ public class WallpaperCropActivity extends Activity {
                 InputStream is = context.getContentResolver().openInputStream(uri);
                 BufferedInputStream bis = new BufferedInputStream(is);
                 ei.readExif(bis);
+                bis.close();
             } else {
                 InputStream is = res.openRawResource(resId);
                 BufferedInputStream bis = new BufferedInputStream(is);
                 ei.readExif(bis);
+                bis.close();
             }
             Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
             if (ori != null) {
@@ -408,7 +429,6 @@ public class WallpaperCropActivity extends Activity {
         String mInFilePath;
         byte[] mInImageBytes;
         int mInResId = 0;
-        InputStream mInStream;
         RectF mCropBounds = null;
         int mOutWidth, mOutHeight;
         int mRotation;
@@ -481,37 +501,36 @@ public class WallpaperCropActivity extends Activity {
         }
 
         // Helper to setup input stream
-        private void regenerateInputStream() {
+        private InputStream regenerateInputStream() {
             if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
                 Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
                         "image byte array given");
             } else {
-                Utils.closeSilently(mInStream);
                 try {
                     if (mInUri != null) {
-                        mInStream = new BufferedInputStream(
+                        return new BufferedInputStream(
                                 mContext.getContentResolver().openInputStream(mInUri));
                     } else if (mInFilePath != null) {
-                        mInStream = mContext.openFileInput(mInFilePath);
+                        return mContext.openFileInput(mInFilePath);
                     } else if (mInImageBytes != null) {
-                        mInStream = new BufferedInputStream(
-                                new ByteArrayInputStream(mInImageBytes));
+                        return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
                     } else {
-                        mInStream = new BufferedInputStream(
-                                mResources.openRawResource(mInResId));
+                        return new BufferedInputStream(mResources.openRawResource(mInResId));
                     }
                 } catch (FileNotFoundException e) {
                     Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
                 }
             }
+            return null;
         }
 
         public Point getImageBounds() {
-            regenerateInputStream();
-            if (mInStream != null) {
+            InputStream is = regenerateInputStream();
+            if (is != null) {
                 BitmapFactory.Options options = new BitmapFactory.Options();
                 options.inJustDecodeBounds = true;
-                BitmapFactory.decodeStream(mInStream, null, options);
+                BitmapFactory.decodeStream(is, null, options);
+                Utils.closeSilently(is);
                 if (options.outWidth != 0 && options.outHeight != 0) {
                     return new Point(options.outWidth, options.outHeight);
                 }
@@ -529,22 +548,26 @@ public class WallpaperCropActivity extends Activity {
         public boolean cropBitmap() {
             boolean failure = false;
 
-            regenerateInputStream();
 
             WallpaperManager wallpaperManager = null;
             if (mSetWallpaper) {
                 wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
             }
-            if (mSetWallpaper && mNoCrop && mInStream != null) {
+
+
+            if (mSetWallpaper && mNoCrop) {
                 try {
-                    wallpaperManager.setStream(mInStream);
+                    InputStream is = regenerateInputStream();
+                    if (is != null) {
+                        wallpaperManager.setStream(is);
+                        Utils.closeSilently(is);
+                    }
                 } catch (IOException e) {
                     Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                     failure = true;
                 }
                 return !failure;
-            }
-            if (mInStream != null) {
+            } else {
                 // Find crop bounds (scaled to original image size)
                 Rect roundedTrueCrop = new Rect();
                 Matrix rotateMatrix = new Matrix();
@@ -557,6 +580,11 @@ public class WallpaperCropActivity extends Activity {
                     mCropBounds = new RectF(roundedTrueCrop);
 
                     Point bounds = getImageBounds();
+                    if (bounds == null) {
+                        Log.w(LOGTAG, "cannot get bounds for image");
+                        failure = true;
+                        return false;
+                    }
 
                     float[] rotatedBounds = new float[] { bounds.x, bounds.y };
                     rotateMatrix.mapPoints(rotatedBounds);
@@ -567,7 +595,6 @@ public class WallpaperCropActivity extends Activity {
                     inverseRotateMatrix.mapRect(mCropBounds);
                     mCropBounds.offset(bounds.x/2, bounds.y/2);
 
-                    regenerateInputStream();
                 }
 
                 mCropBounds.roundOut(roundedTrueCrop);
@@ -585,7 +612,14 @@ public class WallpaperCropActivity extends Activity {
                 // Attempt to open a region decoder
                 BitmapRegionDecoder decoder = null;
                 try {
-                    decoder = BitmapRegionDecoder.newInstance(mInStream, true);
+                    InputStream is = regenerateInputStream();
+                    if (is == null) {
+                        Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+                        failure = true;
+                        return false;
+                    }
+                    decoder = BitmapRegionDecoder.newInstance(is, false);
+                    Utils.closeSilently(is);
                 } catch (IOException e) {
                     Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
                 }
@@ -603,14 +637,15 @@ public class WallpaperCropActivity extends Activity {
 
                 if (crop == null) {
                     // BitmapRegionDecoder has failed, try to crop in-memory
-                    regenerateInputStream();
+                    InputStream is = regenerateInputStream();
                     Bitmap fullSize = null;
-                    if (mInStream != null) {
+                    if (is != null) {
                         BitmapFactory.Options options = new BitmapFactory.Options();
                         if (scaleDownSampleSize > 1) {
                             options.inSampleSize = scaleDownSampleSize;
                         }
-                        fullSize = BitmapFactory.decodeStream(mInStream, null, options);
+                        fullSize = BitmapFactory.decodeStream(is, null, options);
+                        Utils.closeSilently(is);
                     }
                     if (fullSize != null) {
                         mCropBounds.left /= scaleDownSampleSize;
index c58d660..efc3110 100644 (file)
@@ -67,8 +67,10 @@ import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
+import android.widget.Toast;
 
 import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -84,7 +86,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
     private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
     private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg";
 
-    private View mSelectedThumb;
+    private View mSelectedTile;
     private boolean mIgnoreNextTap;
     private OnClickListener mThumbnailOnClickListener;
 
@@ -128,13 +130,39 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
 
     public static class UriWallpaperInfo extends WallpaperTileInfo {
         private Uri mUri;
+        private boolean mFirstClick = true;
+        private BitmapRegionTileSource.UriBitmapSource mBitmapSource;
         public UriWallpaperInfo(Uri uri) {
             mUri = uri;
         }
         @Override
-        public void onClick(WallpaperPickerActivity a) {
-            a.setCropViewTileSource(new BitmapRegionTileSource.UriBitmapSource(
-                    a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE), true, false);
+        public void onClick(final WallpaperPickerActivity a) {
+            final Runnable onLoad;
+            if (!mFirstClick) {
+                onLoad = null;
+            } else {
+                mFirstClick = false;
+                onLoad = new Runnable() {
+                    public void run() {
+                        if (mBitmapSource != null &&
+                                mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                            mView.setVisibility(View.VISIBLE);
+                            a.selectTile(mView);
+                        } else {
+                            ViewGroup parent = (ViewGroup) mView.getParent();
+                            if (parent != null) {
+                                parent.removeView(mView);
+                                Toast.makeText(a,
+                                        a.getString(R.string.image_load_fail),
+                                        Toast.LENGTH_SHORT).show();
+                            }
+                        }
+                    }
+                };
+            }
+            mBitmapSource = new BitmapRegionTileSource.UriBitmapSource(
+                    a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+            a.setCropViewTileSource(mBitmapSource, true, false, onLoad);
         }
         @Override
         public void onSave(final WallpaperPickerActivity a) {
@@ -304,17 +332,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                     return;
                 }
                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
-                if (info.isSelectable()) {
-                    if (mSelectedThumb != null) {
-                        mSelectedThumb.setSelected(false);
-                        mSelectedThumb = null;
-                    }
-                    mSelectedThumb = v;
-                    v.setSelected(true);
-                    // TODO: Remove this once the accessibility framework and
-                    // services have better support for selection state.
-                    v.announceForAccessibility(
-                            getString(R.string.announce_selection, v.getContentDescription()));
+                if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
+                    selectTile(v);
                 }
                 info.onClick(WallpaperPickerActivity.this);
             }
@@ -440,8 +459,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                 new View.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        if (mSelectedThumb != null) {
-                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedThumb.getTag();
+                        if (mSelectedTile != null) {
+                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
                             info.onSave(WallpaperPickerActivity.this);
                         }
                     }
@@ -520,15 +539,23 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
                     c.setChecked(false);
                 }
-                mSelectedThumb.setSelected(true);
+                mSelectedTile.setSelected(true);
                 mActionMode = null;
             }
         };
     }
-    @Override
-    public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
-            final boolean touchEnabled, boolean moveToLeft) {
-        super.setCropViewTileSource(bitmapSource, touchEnabled, moveToLeft);
+
+    private void selectTile(View v) {
+        if (mSelectedTile != null) {
+            mSelectedTile.setSelected(false);
+            mSelectedTile = null;
+        }
+        mSelectedTile = v;
+        v.setSelected(true);
+        // TODO: Remove this once the accessibility framework and
+        // services have better support for selection state.
+        v.announceForAccessibility(
+                getString(R.string.announce_selection, v.getContentDescription()));
     }
 
     private void initializeScrollForRtl() {
@@ -692,8 +719,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
     private void addTemporaryWallpaperTile(final Uri uri) {
         mTempWallpaperTiles.add(uri);
         // Add a tile for the image picked from Gallery
-        FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
+        final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+        pickedImageThumbnail.setVisibility(View.GONE);
         setWallpaperItemPaddingToZero(pickedImageThumbnail);
         mWallpapersView.addView(pickedImageThumbnail, 0);
 
index b5774f4..b85caaa 100644 (file)
@@ -31,6 +31,7 @@ import android.os.Build.VERSION_CODES;
 import android.util.Log;
 
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.exif.ExifInterface;
 import com.android.gallery3d.glrenderer.BasicTexture;
 import com.android.gallery3d.glrenderer.BitmapTexture;
@@ -62,10 +63,12 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         private Bitmap mPreview;
         private int mPreviewSize;
         private int mRotation;
+        public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
+        private State mState = State.NOT_LOADED;
         public BitmapSource(int previewSize) {
             mPreviewSize = previewSize;
         }
-        public void loadInBackground() {
+        public boolean loadInBackground() {
             ExifInterface ei = new ExifInterface();
             if (readExif(ei)) {
                 Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
@@ -74,21 +77,32 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
                 }
             }
             mDecoder = loadBitmapRegionDecoder();
-            int width = mDecoder.getWidth();
-            int height = mDecoder.getHeight();
-            if (mPreviewSize != 0) {
-                int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
-                BitmapFactory.Options opts = new BitmapFactory.Options();
-                opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                opts.inPreferQualityOverSpeed = true;
-
-                float scale = (float) previewSize / Math.max(width, height);
-                opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
-                opts.inJustDecodeBounds = false;
-                mPreview = loadPreviewBitmap(opts);
+            if (mDecoder == null) {
+                mState = State.ERROR_LOADING;
+                return false;
+            } else {
+                int width = mDecoder.getWidth();
+                int height = mDecoder.getHeight();
+                if (mPreviewSize != 0) {
+                    int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
+                    BitmapFactory.Options opts = new BitmapFactory.Options();
+                    opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                    opts.inPreferQualityOverSpeed = true;
+
+                    float scale = (float) previewSize / Math.max(width, height);
+                    opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+                    opts.inJustDecodeBounds = false;
+                    mPreview = loadPreviewBitmap(opts);
+                }
+                mState = State.LOADED;
+                return true;
             }
         }
 
+        public State getLoadingState() {
+            return mState;
+        }
+
         public BitmapRegionDecoder getBitmapRegionDecoder() {
             return mDecoder;
         }
@@ -156,7 +170,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         @Override
         public BitmapRegionDecoder loadBitmapRegionDecoder() {
             try {
-                return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
+                InputStream is = regenerateInputStream();
+                BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(is, false);
+                Utils.closeSilently(is);
+                return regionDecoder;
             } catch (FileNotFoundException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return null;
@@ -168,7 +185,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         @Override
         public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
             try {
-                return BitmapFactory.decodeStream(regenerateInputStream(), null, options);
+                InputStream is = regenerateInputStream();
+                Bitmap b = BitmapFactory.decodeStream(is, null, options);
+                Utils.closeSilently(is);
+                return b;
             } catch (FileNotFoundException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return null;
@@ -177,13 +197,15 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         @Override
         public boolean readExif(ExifInterface ei) {
             try {
-                ei.readExif(regenerateInputStream());
+                InputStream is = regenerateInputStream();
+                ei.readExif(is);
+                Utils.closeSilently(is);
                 return true;
             } catch (FileNotFoundException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return false;
             } catch (IOException e) {
-                Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return false;
             }
         }
@@ -204,7 +226,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         @Override
         public BitmapRegionDecoder loadBitmapRegionDecoder() {
             try {
-                return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
+                InputStream is = regenerateInputStream();
+                BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(is, true);
+                Utils.closeSilently(is);
+                return regionDecoder;
             } catch (IOException e) {
                 Log.e("BitmapRegionTileSource", "Error reading resource", e);
                 return null;
@@ -217,7 +242,9 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         @Override
         public boolean readExif(ExifInterface ei) {
             try {
-                ei.readExif(regenerateInputStream());
+                InputStream is = regenerateInputStream();
+                ei.readExif(is);
+                Utils.closeSilently(is);
                 return true;
             } catch (IOException e) {
                 Log.e("BitmapRegionTileSource", "Error reading resource", e);
@@ -243,27 +270,29 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
         mTileSize = TiledImageRenderer.suggestedTileSize(context);
         mRotation = source.getRotation();
         mDecoder = source.getBitmapRegionDecoder();
-        mWidth = mDecoder.getWidth();
-        mHeight = mDecoder.getHeight();
-        mOptions = new BitmapFactory.Options();
-        mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        mOptions.inPreferQualityOverSpeed = true;
-        mOptions.inTempStorage = new byte[16 * 1024];
-        int previewSize = source.getPreviewSize();
-        if (previewSize != 0) {
-            previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
-            // Although this is the same size as the Bitmap that is likely already
-            // loaded, the lifecycle is different and interactions are on a different
-            // thread. Thus to simplify, this source will decode its own bitmap.
-            Bitmap preview = decodePreview(source, previewSize);
-            if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
-                mPreview = new BitmapTexture(preview);
-            } else {
-                Log.w(TAG, String.format(
-                        "Failed to create preview of apropriate size! "
-                        + " in: %dx%d, out: %dx%d",
-                        mWidth, mHeight,
-                        preview.getWidth(), preview.getHeight()));
+        if (mDecoder != null) {
+            mWidth = mDecoder.getWidth();
+            mHeight = mDecoder.getHeight();
+            mOptions = new BitmapFactory.Options();
+            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+            mOptions.inPreferQualityOverSpeed = true;
+            mOptions.inTempStorage = new byte[16 * 1024];
+            int previewSize = source.getPreviewSize();
+            if (previewSize != 0) {
+                previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+                // Although this is the same size as the Bitmap that is likely already
+                // loaded, the lifecycle is different and interactions are on a different
+                // thread. Thus to simplify, this source will decode its own bitmap.
+                Bitmap preview = decodePreview(source, previewSize);
+                if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
+                    mPreview = new BitmapTexture(preview);
+                } else {
+                    Log.w(TAG, String.format(
+                            "Failed to create preview of apropriate size! "
+                            + " in: %dx%d, out: %dx%d",
+                            mWidth, mHeight,
+                            preview.getWidth(), preview.getHeight()));
+                }
             }
         }
     }