Load images on a bg thread
Bug: 11134758 Change-Id: Ie789a2fcb2024832ac104cd9cdbc3602abee85ee
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
android:visibility="invisible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateOnly="true"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
android:visibility="invisible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateOnly="true"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
@@ -60,13 +60,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
|
||||
public void onClick(WallpaperPickerActivity a) {
|
||||
String imageFilename = a.getSavedImages().getImageFilename(mDbId);
|
||||
File file = new File(a.getFilesDir(), imageFilename);
|
||||
CropView v = a.getCropView();
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath());
|
||||
a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
|
||||
v.setTileSource(
|
||||
new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null);
|
||||
v.moveToLeft();
|
||||
v.setTouchEnabled(false);
|
||||
BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
|
||||
new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
|
||||
a.setCropViewTileSource(bitmapSource, false, true);
|
||||
}
|
||||
@Override
|
||||
public void onSave(WallpaperPickerActivity a) {
|
||||
|
||||
@@ -37,7 +37,6 @@ import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
@@ -96,9 +95,6 @@ public class WallpaperCropActivity extends Activity {
|
||||
return;
|
||||
}
|
||||
|
||||
int rotation = getRotationFromExif(this, imageUri);
|
||||
mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
|
||||
mCropView.setTouchEnabled(true);
|
||||
// Action bar
|
||||
// Show the custom action bar view
|
||||
final ActionBar actionBar = getActionBar();
|
||||
@@ -111,6 +107,46 @@ public class WallpaperCropActivity extends Activity {
|
||||
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
|
||||
}
|
||||
});
|
||||
|
||||
// Load image in background
|
||||
setCropViewTileSource(
|
||||
new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024), true, false);
|
||||
}
|
||||
|
||||
public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
|
||||
final boolean touchEnabled, final boolean moveToLeft) {
|
||||
final Context context = WallpaperCropActivity.this;
|
||||
final View progressView = findViewById(R.id.loading);
|
||||
final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
|
||||
protected Void doInBackground(Void...args) {
|
||||
if (!isCancelled()) {
|
||||
bitmapSource.loadInBackground();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// We don't want to show the spinner every time we load an image, because that would be
|
||||
// annoying; instead, only start showing the spinner if loading the image has taken
|
||||
// longer than 1 sec (ie 1000 ms)
|
||||
progressView.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
loadBitmapTask.execute();
|
||||
}
|
||||
|
||||
public boolean enableRotation() {
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LevelListDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
@@ -133,11 +134,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
}
|
||||
@Override
|
||||
public void onClick(WallpaperPickerActivity a) {
|
||||
CropView v = a.getCropView();
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
|
||||
a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
|
||||
v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
|
||||
v.setTouchEnabled(true);
|
||||
a.setCropViewTileSource(
|
||||
new BitmapRegionTileSource.UriBitmapSource(a, mUri, 1024), true, false);
|
||||
}
|
||||
@Override
|
||||
public void onSave(final WallpaperPickerActivity a) {
|
||||
@@ -175,9 +173,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
}
|
||||
@Override
|
||||
public void onClick(WallpaperPickerActivity a) {
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
|
||||
BitmapRegionTileSource source = new BitmapRegionTileSource(
|
||||
mResources, a, mResId, 1024, rotation);
|
||||
BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
|
||||
new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId, 1024);
|
||||
bitmapSource.loadInBackground();
|
||||
BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource);
|
||||
CropView v = a.getCropView();
|
||||
a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
|
||||
v.setTileSource(source, null);
|
||||
@@ -528,6 +527,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
|
||||
final boolean touchEnabled, boolean moveToLeft) {
|
||||
getDefaultWallpaperView().setVisibility(View.INVISIBLE);
|
||||
super.setCropViewTileSource(bitmapSource, touchEnabled, moveToLeft);
|
||||
}
|
||||
|
||||
private void initializeScrollForRtl() {
|
||||
final HorizontalScrollView scroll =
|
||||
@@ -687,26 +692,34 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void addTemporaryWallpaperTile(Uri uri) {
|
||||
private void addTemporaryWallpaperTile(final Uri uri) {
|
||||
mTempWallpaperTiles.add(uri);
|
||||
// Add a tile for the image picked from Gallery
|
||||
FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
|
||||
inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
|
||||
setWallpaperItemPaddingToZero(pickedImageThumbnail);
|
||||
mWallpapersView.addView(pickedImageThumbnail, 0);
|
||||
|
||||
// Load the thumbnail
|
||||
ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
|
||||
Point defaultSize = getDefaultThumbnailSize(this.getResources());
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
|
||||
Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
|
||||
if (thumb != null) {
|
||||
image.setImageBitmap(thumb);
|
||||
Drawable thumbDrawable = image.getDrawable();
|
||||
thumbDrawable.setDither(true);
|
||||
} else {
|
||||
Log.e(TAG, "Error loading thumbnail for uri=" + uri);
|
||||
}
|
||||
mWallpapersView.addView(pickedImageThumbnail, 0);
|
||||
final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
|
||||
final Point defaultSize = getDefaultThumbnailSize(this.getResources());
|
||||
final Context context = this;
|
||||
new AsyncTask<Void, Bitmap, Bitmap>() {
|
||||
protected Bitmap doInBackground(Void...args) {
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
|
||||
return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
|
||||
|
||||
}
|
||||
protected void onPostExecute(Bitmap thumb) {
|
||||
if (thumb != null) {
|
||||
image.setImageBitmap(thumb);
|
||||
Drawable thumbDrawable = image.getDrawable();
|
||||
thumbDrawable.setDither(true);
|
||||
} else {
|
||||
Log.e(TAG, "Error loading thumbnail for uri=" + uri);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
|
||||
UriWallpaperInfo info = new UriWallpaperInfo(uri);
|
||||
pickedImageThumbnail.setTag(info);
|
||||
|
||||
@@ -31,11 +31,13 @@ import android.os.Build.VERSION_CODES;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.gallery3d.common.BitmapUtils;
|
||||
import com.android.gallery3d.exif.ExifInterface;
|
||||
import com.android.gallery3d.glrenderer.BasicTexture;
|
||||
import com.android.gallery3d.glrenderer.BitmapTexture;
|
||||
import com.android.photos.views.TiledImageRenderer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -55,6 +57,175 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
|
||||
// due to decodePreview being allowed to be up to 2x the size of the target
|
||||
private static final int MAX_PREVIEW_SIZE = 1024;
|
||||
|
||||
public static abstract class BitmapSource {
|
||||
private BitmapRegionDecoder mDecoder;
|
||||
private Bitmap mPreview;
|
||||
private int mPreviewSize;
|
||||
private int mRotation;
|
||||
public BitmapSource(int previewSize) {
|
||||
mPreviewSize = previewSize;
|
||||
}
|
||||
public void loadInBackground() {
|
||||
ExifInterface ei = new ExifInterface();
|
||||
if (readExif(ei)) {
|
||||
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
|
||||
if (ori != null) {
|
||||
mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapRegionDecoder getBitmapRegionDecoder() {
|
||||
return mDecoder;
|
||||
}
|
||||
|
||||
public Bitmap getPreviewBitmap() {
|
||||
return mPreview;
|
||||
}
|
||||
|
||||
public int getPreviewSize() {
|
||||
return mPreviewSize;
|
||||
}
|
||||
|
||||
public int getRotation() {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
public abstract boolean readExif(ExifInterface ei);
|
||||
public abstract BitmapRegionDecoder loadBitmapRegionDecoder();
|
||||
public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
|
||||
}
|
||||
|
||||
public static class FilePathBitmapSource extends BitmapSource {
|
||||
private String mPath;
|
||||
public FilePathBitmapSource(String path, int previewSize) {
|
||||
super(previewSize);
|
||||
mPath = path;
|
||||
}
|
||||
@Override
|
||||
public BitmapRegionDecoder loadBitmapRegionDecoder() {
|
||||
try {
|
||||
return BitmapRegionDecoder.newInstance(mPath, true);
|
||||
} catch (IOException e) {
|
||||
Log.w("BitmapRegionTileSource", "getting decoder failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
|
||||
return BitmapFactory.decodeFile(mPath, options);
|
||||
}
|
||||
@Override
|
||||
public boolean readExif(ExifInterface ei) {
|
||||
try {
|
||||
ei.readExif(mPath);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w("BitmapRegionTileSource", "getting decoder failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UriBitmapSource extends BitmapSource {
|
||||
private Context mContext;
|
||||
private Uri mUri;
|
||||
public UriBitmapSource(Context context, Uri uri, int previewSize) {
|
||||
super(previewSize);
|
||||
mContext = context;
|
||||
mUri = uri;
|
||||
}
|
||||
private InputStream regenerateInputStream() throws FileNotFoundException {
|
||||
InputStream is = mContext.getContentResolver().openInputStream(mUri);
|
||||
return new BufferedInputStream(is);
|
||||
}
|
||||
@Override
|
||||
public BitmapRegionDecoder loadBitmapRegionDecoder() {
|
||||
try {
|
||||
return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
|
||||
try {
|
||||
return BitmapFactory.decodeStream(regenerateInputStream(), null, options);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean readExif(ExifInterface ei) {
|
||||
try {
|
||||
ei.readExif(regenerateInputStream());
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResourceBitmapSource extends BitmapSource {
|
||||
private Resources mRes;
|
||||
private int mResId;
|
||||
public ResourceBitmapSource(Resources res, int resId, int previewSize) {
|
||||
super(previewSize);
|
||||
mRes = res;
|
||||
mResId = resId;
|
||||
}
|
||||
private InputStream regenerateInputStream() {
|
||||
InputStream is = mRes.openRawResource(mResId);
|
||||
return new BufferedInputStream(is);
|
||||
}
|
||||
@Override
|
||||
public BitmapRegionDecoder loadBitmapRegionDecoder() {
|
||||
try {
|
||||
return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
|
||||
} catch (IOException e) {
|
||||
Log.e("BitmapRegionTileSource", "Error reading resource", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
|
||||
return BitmapFactory.decodeResource(mRes, mResId, options);
|
||||
}
|
||||
@Override
|
||||
public boolean readExif(ExifInterface ei) {
|
||||
try {
|
||||
ei.readExif(regenerateInputStream());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.e("BitmapRegionTileSource", "Error reading resource", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapRegionDecoder mDecoder;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
@@ -68,50 +239,23 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
|
||||
private BitmapFactory.Options mOptions;
|
||||
private Canvas mCanvas;
|
||||
|
||||
public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) {
|
||||
this(null, context, path, null, 0, previewSize, rotation);
|
||||
}
|
||||
|
||||
public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) {
|
||||
this(null, context, null, uri, 0, previewSize, rotation);
|
||||
}
|
||||
|
||||
public BitmapRegionTileSource(Resources res,
|
||||
Context context, int resId, int previewSize, int rotation) {
|
||||
this(res, context, null, null, resId, previewSize, rotation);
|
||||
}
|
||||
|
||||
private BitmapRegionTileSource(Resources res,
|
||||
Context context, String path, Uri uri, int resId, int previewSize, int rotation) {
|
||||
public BitmapRegionTileSource(Context context, BitmapSource source) {
|
||||
mTileSize = TiledImageRenderer.suggestedTileSize(context);
|
||||
mRotation = rotation;
|
||||
try {
|
||||
if (path != null) {
|
||||
mDecoder = BitmapRegionDecoder.newInstance(path, true);
|
||||
} else if (uri != null) {
|
||||
InputStream is = context.getContentResolver().openInputStream(uri);
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
mDecoder = BitmapRegionDecoder.newInstance(bis, true);
|
||||
} else {
|
||||
InputStream is = res.openRawResource(resId);
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
mDecoder = BitmapRegionDecoder.newInstance(bis, true);
|
||||
}
|
||||
mWidth = mDecoder.getWidth();
|
||||
mHeight = mDecoder.getHeight();
|
||||
} catch (IOException e) {
|
||||
Log.w("BitmapRegionTileSource", "ctor failed", e);
|
||||
}
|
||||
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(res, context, path, uri, resId, previewSize);
|
||||
Bitmap preview = decodePreview(source, previewSize);
|
||||
if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
|
||||
mPreview = new BitmapTexture(preview);
|
||||
} else {
|
||||
@@ -215,33 +359,15 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
|
||||
* Note that the returned bitmap may have a long edge that's longer
|
||||
* than the targetSize, but it will always be less than 2x the targetSize
|
||||
*/
|
||||
private Bitmap decodePreview(
|
||||
Resources res, Context context, String file, Uri uri, int resId, int targetSize) {
|
||||
float scale = (float) targetSize / Math.max(mWidth, mHeight);
|
||||
mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
|
||||
mOptions.inJustDecodeBounds = false;
|
||||
|
||||
Bitmap result = null;
|
||||
if (file != null) {
|
||||
result = BitmapFactory.decodeFile(file, mOptions);
|
||||
} else if (uri != null) {
|
||||
try {
|
||||
InputStream is = context.getContentResolver().openInputStream(uri);
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
result = BitmapFactory.decodeStream(bis, null, mOptions);
|
||||
} catch (IOException e) {
|
||||
Log.w("BitmapRegionTileSource", "getting preview failed", e);
|
||||
}
|
||||
} else {
|
||||
result = BitmapFactory.decodeResource(res, resId, mOptions);
|
||||
}
|
||||
private Bitmap decodePreview(BitmapSource source, int targetSize) {
|
||||
Bitmap result = source.getPreviewBitmap();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We need to resize down if the decoder does not support inSampleSize
|
||||
// or didn't support the specified inSampleSize (some decoders only do powers of 2)
|
||||
scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
|
||||
float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
|
||||
|
||||
if (scale <= 0.5) {
|
||||
result = BitmapUtils.resizeBitmapByScale(result, scale, true);
|
||||
|
||||
Reference in New Issue
Block a user