import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.provider.MediaStore;
import android.util.Log;
+import com.adobe.xmp.XMPException;
+import com.adobe.xmp.XMPMeta;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInvalidFormatException;
+import com.android.gallery3d.exif.ExifParser;
+import com.android.gallery3d.exif.ExifTag;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.HistoryAdapter;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.tools.BitmapTask;
import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.util.InterruptableOutputStream;
import com.android.gallery3d.util.XmpUtilHelper;
-import java.io.Closeable;
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.util.Vector;
+import java.util.concurrent.locks.ReentrantLock;
+
+// TODO: this class has waaaay to much bitmap copying. Cleanup.
public class ImageLoader {
private static final String LOGTAG = "ImageLoader";
private Bitmap mOriginalBitmapLarge = null;
private Bitmap mBackgroundBitmap = null;
- private Cache mCache = null;
- private Cache mHiresCache = null;
private final ZoomCache mZoomCache = new ZoomCache();
private int mOrientation = 0;
private FilterShowActivity mActivity = null;
- private static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
- private static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
- private static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
- private static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
- private static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
- private static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
- private static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
- private static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
+ public static final String JPEG_MIME_TYPE = "image/jpeg";
+
+ public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
+ public static final int DEFAULT_COMPRESS_QUALITY = 95;
+
+ public static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
+ public static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
+ public static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
+ public static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
+ public static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
+ public static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
+ public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
+ public static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
+ private static final int BITMAP_LOAD_BACKOUT_ATTEMPTS = 5;
private Context mContext = null;
private Uri mUri = null;
private Rect mOriginalBounds = null;
+ private static int mZoomOrientation = ORI_NORMAL;
+
+ private ReentrantLock mLoadingLock = new ReentrantLock();
public ImageLoader(FilterShowActivity activity, Context context) {
mActivity = activity;
mContext = context;
- mCache = new DelayedPresetCache(this, 30);
- mHiresCache = new DelayedPresetCache(this, 2);
}
- public void loadBitmap(Uri uri,int size) {
+ public static int getZoomOrientation() {
+ return mZoomOrientation;
+ }
+
+ public FilterShowActivity getActivity() {
+ return mActivity;
+ }
+
+ public boolean loadBitmap(Uri uri, int size) {
+ mLoadingLock.lock();
mUri = uri;
mOrientation = getOrientation(mContext, uri);
mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
if (mOriginalBitmapSmall == null) {
// Couldn't read the bitmap, let's exit
- mActivity.cannotLoadImage();
+ mLoadingLock.unlock();
+ return false;
}
mOriginalBitmapLarge = loadScaledBitmap(uri, size);
+ if (mOriginalBitmapLarge == null) {
+ mLoadingLock.unlock();
+ return false;
+ }
updateBitmaps();
+ mLoadingLock.unlock();
+ return true;
}
public Uri getUri() {
public static int getOrientation(Context context, Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
- return getOrientationFromPath(uri.getPath());
+ String mimeType = context.getContentResolver().getType(uri);
+ if (mimeType != ImageLoader.JPEG_MIME_TYPE) {
+ return -1;
+ }
+ String path = uri.getPath();
+ int orientation = -1;
+ InputStream is = null;
+ try {
+ is = new FileInputStream(path);
+ ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ if (event == ExifParser.EVENT_NEW_TAG) {
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifTag.TAG_ORIENTATION) {
+ orientation = (int) tag.getValueAt(0);
+ break;
+ }
+ }
+ event = parser.next();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ExifInvalidFormatException e) {
+ e.printStackTrace();
+ } finally {
+ Utils.closeSilently(is);
+ }
+ return orientation;
}
-
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri,
MediaStore.Images.ImageColumns.ORIENTATION
},
null, null, null);
- if (cursor.moveToNext()){
- int ori = cursor.getInt(0);
-
- switch (ori){
- case 0: return ORI_NORMAL;
- case 90: return ORI_ROTATE_90;
- case 270: return ORI_ROTATE_270;
- case 180: return ORI_ROTATE_180;
- default:
- return -1;
- }
- } else{
+ if (cursor.moveToNext()) {
+ int ori = cursor.getInt(0);
+
+ switch (ori) {
+ case 0:
+ return ORI_NORMAL;
+ case 90:
+ return ORI_ROTATE_90;
+ case 270:
+ return ORI_ROTATE_270;
+ case 180:
+ return ORI_ROTATE_180;
+ default:
+ return -1;
+ }
+ } else {
return -1;
}
- } catch (SQLiteException e){
+ } catch (SQLiteException e) {
+ return ExifInterface.ORIENTATION_UNDEFINED;
+ } catch (IllegalArgumentException e) {
return ExifInterface.ORIENTATION_UNDEFINED;
} finally {
Utils.closeSilently(cursor);
}
}
- static int getOrientationFromPath(String path) {
- int orientation = -1;
- try {
- ExifInterface EXIF = new ExifInterface(path);
- orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION,
- 1);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return orientation;
- }
-
private void updateBitmaps() {
if (mOrientation > 1) {
mOriginalBitmapSmall = rotateToPortrait(mOriginalBitmapSmall, mOrientation);
mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge, mOrientation);
}
- mCache.setOriginalBitmap(mOriginalBitmapSmall);
- mHiresCache.setOriginalBitmap(mOriginalBitmapLarge);
+ mZoomOrientation = mOrientation;
warnListeners();
}
- public static Bitmap rotateToPortrait(Bitmap bitmap,int ori) {
- Matrix matrix = new Matrix();
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- if (ori == ORI_ROTATE_90 ||
- ori == ORI_ROTATE_270 ||
- ori == ORI_TRANSPOSE||
- ori == ORI_TRANSVERSE) {
- int tmp = w;
- w = h;
- h = tmp;
- }
- switch(ori){
- case ORI_ROTATE_90:
- matrix.setRotate(90,w/2f,h/2f);
- break;
- case ORI_ROTATE_180:
- matrix.setRotate(180,w/2f,h/2f);
- break;
- case ORI_ROTATE_270:
- matrix.setRotate(270,w/2f,h/2f);
- break;
- case ORI_FLIP_HOR:
- matrix.preScale(-1, 1);
- break;
- case ORI_FLIP_VERT:
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSPOSE:
- matrix.setRotate(90,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSVERSE:
- matrix.setRotate(270,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_NORMAL:
- default:
- return bitmap;
- }
-
- return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
- bitmap.getHeight(), matrix, true);
+ public Bitmap decodeImage(int id, BitmapFactory.Options options) {
+ return BitmapFactory.decodeResource(mContext.getResources(), id, options);
}
- private void closeStream(Closeable stream) {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
+ public static Bitmap rotateToPortrait(Bitmap bitmap, int ori) {
+ Matrix matrix = new Matrix();
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ if (ori == ORI_ROTATE_90 ||
+ ori == ORI_ROTATE_270 ||
+ ori == ORI_TRANSPOSE ||
+ ori == ORI_TRANSVERSE) {
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+ switch (ori) {
+ case ORI_ROTATE_90:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_180:
+ matrix.setRotate(180, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_270:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ break;
+ case ORI_FLIP_HOR:
+ matrix.preScale(-1, 1);
+ break;
+ case ORI_FLIP_VERT:
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSPOSE:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSVERSE:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_NORMAL:
+ default:
+ return bitmap;
}
+
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+ bitmap.getHeight(), matrix, true);
}
- private Bitmap loadRegionBitmap(Uri uri, Rect bounds) {
+ private Bitmap loadRegionBitmap(Uri uri, BitmapFactory.Options options, Rect bounds) {
InputStream is = null;
try {
is = mContext.getContentResolver().openInputStream(uri);
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
- return decoder.decodeRegion(bounds, null);
+ return decoder.decodeRegion(bounds, options);
} catch (FileNotFoundException e) {
Log.e(LOGTAG, "FileNotFoundException: " + uri);
} catch (Exception e) {
e.printStackTrace();
} finally {
- closeStream(is);
+ Utils.closeSilently(is);
}
return null;
}
+ static final int MAX_BITMAP_DIM = 900;
+
private Bitmap loadScaledBitmap(Uri uri, int size) {
InputStream is = null;
try {
int scale = 1;
while (true) {
- if (width_tmp / 2 < size || height_tmp / 2 < size)
- break;
+ if (width_tmp <= MAX_BITMAP_DIM && height_tmp <= MAX_BITMAP_DIM) {
+ if (width_tmp / 2 < size || height_tmp / 2 < size) {
+ break;
+ }
+ }
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
+ o2.inMutable = true;
- closeStream(is);
+ Utils.closeSilently(is);
is = mContext.getContentResolver().openInputStream(uri);
return BitmapFactory.decodeStream(is, null, o2);
} catch (FileNotFoundException e) {
} catch (Exception e) {
e.printStackTrace();
} finally {
- closeStream(is);
+ Utils.closeSilently(is);
}
return null;
}
}
public void addListener(ImageShow imageShow) {
+ mLoadingLock.lock();
if (!mListeners.contains(imageShow)) {
mListeners.add(imageShow);
}
+ mLoadingLock.unlock();
}
- public void warnListeners() {
- for (int i = 0; i < mListeners.size(); i++) {
- ImageShow imageShow = mListeners.elementAt(i);
- imageShow.updateImage();
- }
+ private void warnListeners() {
+ mActivity.runOnUiThread(mWarnListenersRunnable);
}
- // TODO: this currently does the loading + filtering on the UI thread -- need to
- // move this to a background thread.
+ private Runnable mWarnListenersRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ for (int i = 0; i < mListeners.size(); i++) {
+ ImageShow imageShow = mListeners.elementAt(i);
+ imageShow.imageLoaded();
+ }
+ }
+ };
+
+ // FIXME: this currently does the loading + filtering on the UI thread --
+ // need to move this to a background thread.
public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
- boolean force) {
+ Rect destination, boolean force) {
+ mLoadingLock.lock();
Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
if (force || bmp == null) {
- bmp = loadRegionBitmap(mUri, bounds);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inMutable = true;
+ if (destination != null) {
+ if (bounds.width() > destination.width()) {
+ int sampleSize = 1;
+ int w = bounds.width();
+ while (w > destination.width()) {
+ sampleSize *= 2;
+ w /= sampleSize;
+ }
+ options.inSampleSize = sampleSize;
+ }
+ }
+ bmp = loadRegionBitmap(mUri, options, bounds);
+ if (destination != null) {
+ mLoadingLock.unlock();
+ return bmp;
+ }
if (bmp != null) {
- // TODO: this workaround for RS might not be needed ultimately
- Bitmap bmp2 = bmp.copy(Bitmap.Config.ARGB_8888, true);
float scaleFactor = imagePreset.getScaleFactor();
- imagePreset.setScaleFactor(1.0f);
- bmp2 = imagePreset.apply(bmp2);
+ float scale = (float) bmp.getWidth() / (float) getOriginalBounds().width();
+ imagePreset.setScaleFactor(scale);
+ imagePreset.setupEnvironment();
+ bmp = imagePreset.apply(bmp);
imagePreset.setScaleFactor(scaleFactor);
- mZoomCache.setImage(imagePreset, bounds, bmp2);
- return bmp2;
+ mZoomCache.setImage(imagePreset, bounds, bmp);
+ mLoadingLock.unlock();
+ return bmp;
}
}
+ mLoadingLock.unlock();
return bmp;
}
- // Caching method
- public Bitmap getImageForPreset(ImageShow caller, ImagePreset imagePreset,
- boolean hiRes) {
- if (mOriginalBitmapSmall == null) {
- return null;
- }
- if (mOriginalBitmapLarge == null) {
+ public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
+ File destination) {
+ preset.setQuality(ImagePreset.QUALITY_FINAL);
+ preset.setScaleFactor(1.0f);
+ new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() {
+
+ @Override
+ public void onComplete(Uri result) {
+ filterShowActivity.completeSaveImage(result);
+ }
+
+ }).execute(preset);
+ }
+
+ public static Bitmap loadMutableBitmap(Context context, Uri sourceUri) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ return loadMutableBitmap(context, sourceUri, options);
+ }
+
+ public static Bitmap loadMutableBitmap(Context context, Uri sourceUri,
+ BitmapFactory.Options options) {
+ // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
+ // exist)
+ options.inMutable = true;
+
+ Bitmap bitmap = decodeUriWithBackouts(context, sourceUri, options);
+ if (bitmap == null) {
return null;
}
+ int orientation = ImageLoader.getOrientation(context, sourceUri);
+ bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
+ return bitmap;
+ }
- Bitmap filteredImage = null;
+ public static Bitmap decodeUriWithBackouts(Context context, Uri sourceUri,
+ BitmapFactory.Options options) {
+ boolean noBitmap = true;
+ int num_tries = 0;
+ InputStream is = getInputStream(context, sourceUri);
- if (hiRes) {
- filteredImage = mHiresCache.get(imagePreset);
- } else {
- filteredImage = mCache.get(imagePreset);
+ if (options.inSampleSize < 1) {
+ options.inSampleSize = 1;
}
-
- if (filteredImage == null) {
- if (hiRes) {
- cachePreset(imagePreset, mHiresCache, caller);
- } else {
- cachePreset(imagePreset, mCache, caller);
+ // Stopgap fix for low-memory devices.
+ Bitmap bmap = null;
+ while (noBitmap) {
+ if (is == null) {
+ return null;
+ }
+ try {
+ // Try to decode, downsample if low-memory.
+ bmap = BitmapFactory.decodeStream(is, null, options);
+ noBitmap = false;
+ } catch (java.lang.OutOfMemoryError e) {
+ // Try 5 times before failing for good.
+ if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
+ throw e;
+ }
+ is = null;
+ bmap = null;
+ System.gc();
+ is = getInputStream(context, sourceUri);
+ options.inSampleSize *= 2;
}
}
- return filteredImage;
+ Utils.closeSilently(is);
+ return bmap;
}
- public void resetImageForPreset(ImagePreset imagePreset, ImageShow caller) {
- mHiresCache.reset(imagePreset);
- mCache.reset(imagePreset);
- mZoomCache.reset(imagePreset);
+ private static InputStream getInputStream(Context context, Uri sourceUri) {
+ InputStream is = null;
+ try {
+ is = context.getContentResolver().openInputStream(sourceUri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "could not load bitmap ", e);
+ Utils.closeSilently(is);
+ is = null;
+ }
+ return is;
}
- public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
- File destination) {
- preset.setIsHighQuality(true);
+ public static Bitmap decodeResourceWithBackouts(Resources res, BitmapFactory.Options options,
+ int id) {
+ boolean noBitmap = true;
+ int num_tries = 0;
+ if (options.inSampleSize < 1) {
+ options.inSampleSize = 1;
+ }
+ // Stopgap fix for low-memory devices.
+ Bitmap bmap = null;
+ while (noBitmap) {
+ try {
+ // Try to decode, downsample if low-memory.
+ bmap = BitmapFactory.decodeResource(
+ res, id, options);
+ noBitmap = false;
+ } catch (java.lang.OutOfMemoryError e) {
+ // Try 5 times before failing for good.
+ if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
+ throw e;
+ }
+ bmap = null;
+ System.gc();
+ options.inSampleSize *= 2;
+ }
+ }
+ return bmap;
+ }
+
+ public void returnFilteredResult(ImagePreset preset,
+ final FilterShowActivity filterShowActivity) {
+ preset.setQuality(ImagePreset.QUALITY_FINAL);
preset.setScaleFactor(1.0f);
- new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() {
+
+ BitmapTask.Callbacks<ImagePreset, Bitmap> cb = new BitmapTask.Callbacks<ImagePreset, Bitmap>() {
@Override
- public void onComplete(Uri result) {
- filterShowActivity.completeSaveImage(result);
+ public void onComplete(Bitmap result) {
+ filterShowActivity.onFilteredResult(result);
}
- }).execute(preset);
+ @Override
+ public void onCancel() {
+ }
+
+ @Override
+ public Bitmap onExecute(ImagePreset param) {
+ if (param == null || mUri == null) {
+ return null;
+ }
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ boolean noBitmap = true;
+ int num_tries = 0;
+ if (options.inSampleSize < 1) {
+ options.inSampleSize = 1;
+ }
+ Bitmap bitmap = null;
+ // Stopgap fix for low-memory devices.
+ while (noBitmap) {
+ try {
+ // Try to do bitmap operations, downsample if low-memory
+ bitmap = loadMutableBitmap(mContext, mUri, options);
+ if (bitmap == null) {
+ Log.w(LOGTAG, "Failed to save image!");
+ return null;
+ }
+ param.setupEnvironment();
+ bitmap = param.applyGeometry(bitmap);
+ bitmap = param.apply(bitmap);
+ noBitmap = false;
+ } catch (java.lang.OutOfMemoryError e) {
+ // Try 5 times before failing for good.
+ if (++num_tries >= 5) {
+ throw e;
+ }
+ bitmap = null;
+ System.gc();
+ options.inSampleSize *= 2;
+ }
+ }
+ return bitmap;
+ }
+ };
+
+ (new BitmapTask<ImagePreset, Bitmap>(cb)).execute(preset);
+ }
+
+ private String getFileExtension(String requestFormat) {
+ String outputFormat = (requestFormat == null)
+ ? "jpg"
+ : requestFormat;
+ outputFormat = outputFormat.toLowerCase();
+ return (outputFormat.equals("png") || outputFormat.equals("gif"))
+ ? "png" // We don't support gif compression.
+ : "jpg";
+ }
+
+ private CompressFormat convertExtensionToCompressFormat(String extension) {
+ return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
+ }
+
+ public void saveToUri(Bitmap bmap, Uri uri, final String outputFormat,
+ final FilterShowActivity filterShowActivity) {
+
+ OutputStream out = null;
+ try {
+ out = filterShowActivity.getContentResolver().openOutputStream(uri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "cannot write output", e);
+ out = null;
+ } finally {
+ if (bmap == null || out == null) {
+ return;
+ }
+ }
+
+ final InterruptableOutputStream ios = new InterruptableOutputStream(out);
+
+ BitmapTask.Callbacks<Bitmap, Bitmap> cb = new BitmapTask.Callbacks<Bitmap, Bitmap>() {
+
+ @Override
+ public void onComplete(Bitmap result) {
+ filterShowActivity.done();
+ }
+
+ @Override
+ public void onCancel() {
+ ios.interrupt();
+ }
+
+ @Override
+ public Bitmap onExecute(Bitmap param) {
+ if (param == null) {
+ return null;
+ }
+ CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(outputFormat));
+ param.compress(cf, DEFAULT_COMPRESS_QUALITY, ios);
+ Utils.closeSilently(ios);
+ return null;
+ }
+ };
+
+ (new BitmapTask<Bitmap, Bitmap>(cb)).execute(bmap);
}
public void setAdapter(HistoryAdapter adapter) {
return mAdapter;
}
- private void cachePreset(ImagePreset preset, Cache cache, ImageShow caller) {
- cache.prepare(preset);
- cache.addObserver(caller);
- }
-
- public Object getXmpObject() {
+ public XMPMeta getXmpObject() {
try {
InputStream is = mContext.getContentResolver().openInputStream(getUri());
return XmpUtilHelper.extractXMPMeta(is);
return null;
}
}
+
+ /**
+ * Determine if this is a light cycle 360 image
+ *
+ * @return true if it is a light Cycle image that is full 360
+ */
+ public boolean queryLightCycle360() {
+ InputStream is = null;
+ try {
+ is = mContext.getContentResolver().openInputStream(getUri());
+ XMPMeta meta = XmpUtilHelper.extractXMPMeta(is);
+ if (meta == null) {
+ return false;
+ }
+ String name = meta.getPacketHeader();
+ String namespace = "http://ns.google.com/photos/1.0/panorama/";
+ String cropWidthName = "GPano:CroppedAreaImageWidthPixels";
+ String fullWidthName = "GPano:FullPanoWidthPixels";
+
+ if (!meta.doesPropertyExist(namespace, cropWidthName)) {
+ return false;
+ }
+ if (!meta.doesPropertyExist(namespace, fullWidthName)) {
+ return false;
+ }
+
+ Integer cropValue = meta.getPropertyInteger(namespace, cropWidthName);
+ Integer fullValue = meta.getPropertyInteger(namespace, fullWidthName);
+
+ // Definition of a 360:
+ // GFullPanoWidthPixels == CroppedAreaImageWidthPixels
+ if (cropValue != null && fullValue != null) {
+ return cropValue.equals(fullValue);
+ }
+
+ return false;
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (XMPException e) {
+ return false;
+ } finally {
+ Utils.closeSilently(is);
+ }
+ }
}