From 9a4c2cac83f50e29063d27605c9f8d9e676a6f22 Mon Sep 17 00:00:00 2001 From: Chih-Chung Chang Date: Fri, 5 Mar 2010 15:34:47 -0800 Subject: [PATCH] Remove unused code. --- AndroidManifest.xml | 1 - res/values/attrs.xml | 8 - src/com/android/camera/BitmapCache.java | 126 ----- src/com/android/camera/CameraSettings.java | 1 - src/com/android/camera/EvenlySpacedLayout.java | 130 ----- src/com/android/camera/IconIndicator.java | 1 - src/com/android/camera/ImageGetter.java | 314 ----------- src/com/android/camera/ImageManager.java | 98 +--- src/com/android/camera/ImageViewTouchBase.java | 385 ------------- src/com/android/camera/MenuHelper.java | 612 --------------------- src/com/android/camera/ReverseGeocoderTask.java | 82 --- src/com/android/camera/RotateBitmap.java | 105 ---- src/com/android/camera/Util.java | 88 --- src/com/android/camera/VideoCamera.java | 21 - src/com/android/camera/gallery/BaseImage.java | 96 +--- src/com/android/camera/gallery/BaseImageList.java | 74 --- src/com/android/camera/gallery/IImage.java | 34 -- src/com/android/camera/gallery/IImageList.java | 23 - src/com/android/camera/gallery/Image.java | 119 +--- src/com/android/camera/gallery/ImageList.java | 31 +- src/com/android/camera/gallery/ImageListUber.java | 80 --- .../android/camera/gallery/SingleImageList.java | 67 --- src/com/android/camera/gallery/UriImage.java | 168 ------ src/com/android/camera/gallery/VideoList.java | 29 +- src/com/android/camera/gallery/VideoObject.java | 53 +- .../com/android/camera/BitmapManagerUnitTests.java | 146 ----- 26 files changed, 28 insertions(+), 2864 deletions(-) delete mode 100644 src/com/android/camera/BitmapCache.java delete mode 100644 src/com/android/camera/EvenlySpacedLayout.java delete mode 100644 src/com/android/camera/ImageGetter.java delete mode 100644 src/com/android/camera/ImageViewTouchBase.java delete mode 100644 src/com/android/camera/ReverseGeocoderTask.java delete mode 100644 src/com/android/camera/RotateBitmap.java delete mode 100644 src/com/android/camera/gallery/SingleImageList.java delete mode 100644 src/com/android/camera/gallery/UriImage.java delete mode 100644 tests/src/com/android/camera/BitmapManagerUnitTests.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 87c4217..6051c7e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -18,7 +18,6 @@ - diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 74a6091..25e997a 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -31,12 +31,4 @@ - - - - - - - - diff --git a/src/com/android/camera/BitmapCache.java b/src/com/android/camera/BitmapCache.java deleted file mode 100644 index 3476c71..0000000 --- a/src/com/android/camera/BitmapCache.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.camera; - -import android.graphics.Bitmap; - -class BitmapCache implements ImageViewTouchBase.Recycler { - public static class Entry { - int mPos; - Bitmap mBitmap; - public Entry() { - clear(); - } - public void clear() { - mPos = -1; - mBitmap = null; - } - } - - private final Entry[] mCache; - - public BitmapCache(int size) { - mCache = new Entry[size]; - for (int i = 0; i < mCache.length; i++) { - mCache[i] = new Entry(); - } - } - - // Given the position, find the associated entry. Returns null if there is - // no such entry. - private Entry findEntry(int pos) { - for (Entry e : mCache) { - if (pos == e.mPos) { - return e; - } - } - return null; - } - - // Returns the thumb bitmap if we have it, otherwise return null. - public synchronized Bitmap getBitmap(int pos) { - Entry e = findEntry(pos); - if (e != null) { - return e.mBitmap; - } - return null; - } - - public synchronized void put(int pos, Bitmap bitmap) { - // First see if we already have this entry. - if (findEntry(pos) != null) { - return; - } - - // Find the best entry we should replace. - // See if there is any empty entry. - // Otherwise assuming sequential access, kick out the entry with the - // greatest distance. - Entry best = null; - int maxDist = -1; - for (Entry e : mCache) { - if (e.mPos == -1) { - best = e; - break; - } else { - int dist = Math.abs(pos - e.mPos); - if (dist > maxDist) { - maxDist = dist; - best = e; - } - } - } - - // Recycle the image being kicked out. - // This only works because our current usage is sequential, so we - // do not happen to recycle the image being displayed. - if (best.mBitmap != null) { - best.mBitmap.recycle(); - } - - best.mPos = pos; - best.mBitmap = bitmap; - } - - // Recycle all bitmaps in the cache and clear the cache. - public synchronized void clear() { - for (Entry e : mCache) { - if (e.mBitmap != null) { - e.mBitmap.recycle(); - } - e.clear(); - } - } - - // Returns whether the bitmap is in the cache. - public synchronized boolean hasBitmap(int pos) { - Entry e = findEntry(pos); - return (e != null); - } - - // Recycle the bitmap if it's not in the cache. - // The input must be non-null. - public synchronized void recycle(Bitmap b) { - for (Entry e : mCache) { - if (e.mPos != -1) { - if (e.mBitmap == b) { - return; - } - } - } - b.recycle(); - } -} diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index b6ad38c..b370883 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.media.CamcorderProfile; -import android.media.CamcorderProfile.Quality; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; import android.preference.PreferenceManager; diff --git a/src/com/android/camera/EvenlySpacedLayout.java b/src/com/android/camera/EvenlySpacedLayout.java deleted file mode 100644 index ec0f495..0000000 --- a/src/com/android/camera/EvenlySpacedLayout.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -/** - * A layout which makes the children evenly spaced (horizontally or vertically). - * - *

Currently it does not consider the padding parameters. - */ -public class EvenlySpacedLayout extends ViewGroup { - private boolean mHorizontal; - - // Wheather we keep the space in both ends of the layout - private boolean mKeepEndSpace; - - public EvenlySpacedLayout(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.EvenlySpacedLayout, 0, 0); - mHorizontal = (0 == a.getInt( - R.styleable.EvenlySpacedLayout_orientation, 0)); - mKeepEndSpace = a.getBoolean( - R.styleable.EvenlySpacedLayout_keepEndSpace, true); - a.recycle(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int count = getChildCount(); - int width = 0; - int height = 0; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() == GONE) continue; - measureChild(child, widthMeasureSpec, heightMeasureSpec); - if (mHorizontal) { - width += child.getMeasuredWidth(); - height = Math.max(height, child.getMeasuredHeight()); - } else { - height += child.getMeasuredHeight(); - width = Math.max(width, child.getMeasuredWidth()); - } - } - setMeasuredDimension(resolveSize(width, widthMeasureSpec), - resolveSize(height, heightMeasureSpec)); - } - - private void layoutHorizontal(boolean changed, int l, int t, int r, int b) { - int count = getChildCount(); - - int usedWidth = 0; - int usedChildren = 0; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() == GONE) continue; - usedWidth += child.getMeasuredWidth(); - ++usedChildren; - } - - int spacing = (r - l - usedWidth) / - (mKeepEndSpace ? (usedChildren + 1) : (usedChildren - 1)); - int left = mKeepEndSpace ? spacing : 0; - int top = 0; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() == GONE) continue; - int w = child.getMeasuredWidth(); - int h = child.getMeasuredHeight(); - child.layout(left, top, left + w, top + h); - left += w; - left += spacing; - } - } - - private void layoutVertical(boolean changed, int l, int t, int r, int b) { - int count = getChildCount(); - - int usedHeight = 0; - int usedChildren = 0; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() == GONE) continue; - usedHeight += child.getMeasuredHeight(); - ++usedChildren; - } - - int spacing = (b - t - usedHeight) / - (mKeepEndSpace ? (usedChildren + 1) : (usedChildren - 1)); - int top = mKeepEndSpace ? spacing : 0; - int left = 0; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() == GONE) continue; - int w = child.getMeasuredWidth(); - int h = child.getMeasuredHeight(); - child.layout(left, top, left + w, top + h); - top += h; - top += spacing; - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - if (mHorizontal) { - layoutHorizontal(changed, l, t, r, b); - } else { - layoutVertical(changed, l, t, r, b); - } - } -} diff --git a/src/com/android/camera/IconIndicator.java b/src/com/android/camera/IconIndicator.java index 3d789bc..6563513 100644 --- a/src/com/android/camera/IconIndicator.java +++ b/src/com/android/camera/IconIndicator.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.view.View; import android.widget.ImageView; /** diff --git a/src/com/android/camera/ImageGetter.java b/src/com/android/camera/ImageGetter.java deleted file mode 100644 index 24c9d98..0000000 --- a/src/com/android/camera/ImageGetter.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import com.android.camera.gallery.IImage; -import com.android.camera.gallery.IImageList; -import com.android.camera.gallery.VideoObject; - -import android.content.ContentResolver; -import android.graphics.Bitmap; -import android.os.Handler; -import android.os.Message; -import android.os.Process; - -/* - * Here's the loading strategy. For any given image, load the thumbnail - * into memory and post a callback to display the resulting bitmap. - * - * Then proceed to load the full image bitmap. Three things can - * happen at this point: - * - * 1. the image fails to load because the UI thread decided - * to move on to a different image. This "cancellation" happens - * by virtue of the UI thread closing the stream containing the - * image being decoded. BitmapFactory.decodeStream returns null - * in this case. - * - * 2. the image loaded successfully. At that point we post - * a callback to the UI thread to actually show the bitmap. - * - * 3. when the post runs it checks to see if the image that was - * loaded is still the one we want. The UI may have moved on - * to some other image and if so we just drop the newly loaded - * bitmap on the floor. - */ - -interface ImageGetterCallback { - public void imageLoaded(int pos, int offset, RotateBitmap bitmap, - boolean isThumb); - public boolean wantsThumbnail(int pos, int offset); - public boolean wantsFullImage(int pos, int offset); - public int fullImageSizeToUse(int pos, int offset); - public void completed(); - public int [] loadOrder(); -} - -class ImageGetter { - - @SuppressWarnings("unused") - private static final String TAG = "ImageGetter"; - - // The thread which does the work. - private Thread mGetterThread; - - // The current request serial number. - // This is increased by one each time a new job is assigned. - // It is only written in the main thread. - private int mCurrentSerial; - - // The base position that's being retrieved. The actual images retrieved - // are this base plus each of the offets. -1 means there is no current - // request needs to be finished. - private int mCurrentPosition = -1; - - // The callback to invoke for each image. - private ImageGetterCallback mCB; - - // The image list for the images. - private IImageList mImageList; - - // The handler to do callback. - private GetterHandler mHandler; - - // True if we want to cancel the current loading. - private volatile boolean mCancel = true; - - // True if the getter thread is idle waiting. - private boolean mIdle = false; - - // True when the getter thread should exit. - private boolean mDone = false; - - private ContentResolver mCr; - - private class ImageGetterRunnable implements Runnable { - - private Runnable callback(final int position, final int offset, - final boolean isThumb, - final RotateBitmap bitmap, - final int requestSerial) { - return new Runnable() { - public void run() { - // check for inflight callbacks that aren't applicable - // any longer before delivering them - if (requestSerial == mCurrentSerial) { - mCB.imageLoaded(position, offset, bitmap, isThumb); - } else if (bitmap != null) { - bitmap.recycle(); - } - } - }; - } - - private Runnable completedCallback(final int requestSerial) { - return new Runnable() { - public void run() { - if (requestSerial == mCurrentSerial) { - mCB.completed(); - } - } - }; - } - - public void run() { - // Lower the priority of this thread to avoid competing with - // the UI thread. - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - - while (true) { - synchronized (ImageGetter.this) { - while (mCancel || mDone || mCurrentPosition == -1) { - if (mDone) return; - mIdle = true; - ImageGetter.this.notify(); - try { - ImageGetter.this.wait(); - } catch (InterruptedException ex) { - // ignore - } - mIdle = false; - } - } - - executeRequest(); - - synchronized (ImageGetter.this) { - mCurrentPosition = -1; - } - } - } - private void executeRequest() { - int imageCount = mImageList.getCount(); - - int [] order = mCB.loadOrder(); - for (int i = 0; i < order.length; i++) { - if (mCancel) return; - int offset = order[i]; - int imageNumber = mCurrentPosition + offset; - if (imageNumber >= 0 && imageNumber < imageCount) { - if (!mCB.wantsThumbnail(mCurrentPosition, offset)) { - continue; - } - - IImage image = mImageList.getImageAt(imageNumber); - if (image == null) continue; - if (mCancel) return; - - Bitmap b = image.thumbBitmap(IImage.NO_ROTATE); - if (b == null) continue; - if (mCancel) { - b.recycle(); - return; - } - - Runnable cb = callback(mCurrentPosition, offset, - true, - new RotateBitmap(b, image.getDegreesRotated()), - mCurrentSerial); - mHandler.postGetterCallback(cb); - } - } - - for (int i = 0; i < order.length; i++) { - if (mCancel) return; - int offset = order[i]; - int imageNumber = mCurrentPosition + offset; - if (imageNumber >= 0 && imageNumber < imageCount) { - if (!mCB.wantsFullImage(mCurrentPosition, offset)) { - continue; - } - - IImage image = mImageList.getImageAt(imageNumber); - if (image == null) continue; - if (image instanceof VideoObject) continue; - if (mCancel) return; - - int sizeToUse = mCB.fullImageSizeToUse( - mCurrentPosition, offset); - Bitmap b = image.fullSizeBitmap(sizeToUse, 3 * 1024 * 1024, - IImage.NO_ROTATE); - - if (b == null) continue; - if (mCancel) { - b.recycle(); - return; - } - - RotateBitmap rb = new RotateBitmap(b, - image.getDegreesRotated()); - - Runnable cb = callback(mCurrentPosition, offset, - false, rb, mCurrentSerial); - mHandler.postGetterCallback(cb); - } - } - - mHandler.postGetterCallback(completedCallback(mCurrentSerial)); - } - } - - public ImageGetter(ContentResolver cr) { - mCr = cr; - mGetterThread = new Thread(new ImageGetterRunnable()); - mGetterThread.setName("ImageGettter"); - mGetterThread.start(); - } - - // Cancels current loading (without waiting). - public synchronized void cancelCurrent() { - Util.Assert(mGetterThread != null); - mCancel = true; - BitmapManager.instance().cancelThreadDecoding(mGetterThread, mCr); - } - - // Cancels current loading (with waiting). - private synchronized void cancelCurrentAndWait() { - cancelCurrent(); - while (mIdle != true) { - try { - wait(); - } catch (InterruptedException ex) { - // ignore. - } - } - } - - // Stops this image getter. - public void stop() { - synchronized (this) { - cancelCurrentAndWait(); - mDone = true; - notify(); - } - try { - mGetterThread.join(); - } catch (InterruptedException ex) { - // Ignore the exception - } - mGetterThread = null; - } - - public synchronized void setPosition(int position, ImageGetterCallback cb, - IImageList imageList, GetterHandler handler) { - // Cancel the previous request. - cancelCurrentAndWait(); - - // Set new data. - mCurrentPosition = position; - mCB = cb; - mImageList = imageList; - mHandler = handler; - mCurrentSerial += 1; - - // Kick-start the current request. - mCancel = false; - BitmapManager.instance().allowThreadDecoding(mGetterThread); - notify(); - } -} - -class GetterHandler extends Handler { - private static final int IMAGE_GETTER_CALLBACK = 1; - - @Override - public void handleMessage(Message message) { - switch(message.what) { - case IMAGE_GETTER_CALLBACK: - ((Runnable) message.obj).run(); - break; - } - } - - public void postGetterCallback(Runnable callback) { - postDelayedGetterCallback(callback, 0); - } - - public void postDelayedGetterCallback(Runnable callback, long delay) { - if (callback == null) { - throw new NullPointerException(); - } - Message message = Message.obtain(); - message.what = IMAGE_GETTER_CALLBACK; - message.obj = callback; - sendMessageDelayed(message, delay); - } - - public void removeAllGetterCallbacks() { - removeMessages(IMAGE_GETTER_CALLBACK); - } -} diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java index 958e499..7a81be8 100644 --- a/src/com/android/camera/ImageManager.java +++ b/src/com/android/camera/ImageManager.java @@ -21,9 +21,7 @@ import com.android.camera.gallery.IImage; import com.android.camera.gallery.IImageList; import com.android.camera.gallery.ImageList; import com.android.camera.gallery.ImageListUber; -import com.android.camera.gallery.SingleImageList; import com.android.camera.gallery.VideoList; -import com.android.camera.gallery.VideoObject; import android.content.ContentResolver; import android.content.ContentValues; @@ -76,9 +74,6 @@ public class ImageManager { public int mSort; public String mBucketId; - // This is only used if we are creating a single image list. - public Uri mSingleImageUri; - // This is only used if we are creating an empty image list. public boolean mIsEmptyImageList; @@ -90,7 +85,6 @@ public class ImageManager { out.writeInt(mInclusion); out.writeInt(mSort); out.writeString(mBucketId); - out.writeParcelable(mSingleImageUri, flags); out.writeInt(mIsEmptyImageList ? 1 : 0); } @@ -99,14 +93,13 @@ public class ImageManager { mInclusion = in.readInt(); mSort = in.readInt(); mBucketId = in.readString(); - mSingleImageUri = in.readParcelable(null); mIsEmptyImageList = (in.readInt() != 0); } public String toString() { return String.format("ImageListParam{loc=%s,inc=%d,sort=%d," + - "bucket=%s,empty=%b,single=%s}", mLocation, mInclusion, - mSort, mBucketId, mIsEmptyImageList, mSingleImageUri); + "bucket=%s,empty=%b}", mLocation, mInclusion, + mSort, mBucketId, mIsEmptyImageList); } public static final Parcelable.Creator CREATOR @@ -188,38 +181,6 @@ public class ImageManager { return retVal; } - /** - * @return true if the mimetype is an image mimetype. - */ - public static boolean isImageMimeType(String mimeType) { - return mimeType.startsWith("image/"); - } - - /** - * @return true if the mimetype is a video mimetype. - */ - /* This is commented out because isVideo is not calling this now. - public static boolean isVideoMimeType(String mimeType) { - return mimeType.startsWith("video/"); - } - */ - - /** - * @return true if the image is an image. - */ - public static boolean isImage(IImage image) { - return isImageMimeType(image.getMimeType()); - } - - /** - * @return true if the image is a video. - */ - public static boolean isVideo(IImage image) { - // This is the right implementation, but we use instanceof for speed. - //return isVideoMimeType(image.getMimeType()); - return (image instanceof VideoObject); - } - public static void setImageSize(ContentResolver cr, Uri uri, long size) { ContentValues values = new ContentValues(); values.put(Images.Media.SIZE, size); @@ -319,17 +280,12 @@ public class ImageManager { int inclusion = param.mInclusion; int sort = param.mSort; String bucketId = param.mBucketId; - Uri singleImageUri = param.mSingleImageUri; boolean isEmptyImageList = param.mIsEmptyImageList; if (isEmptyImageList || cr == null) { return new EmptyImageList(); } - if (singleImageUri != null) { - return new SingleImageList(cr, singleImageUri); - } - // false ==> don't require write access boolean haveSdCard = hasStorage(false); @@ -372,30 +328,6 @@ public class ImageManager { return uber; } - // This is a convenience function to create an image list from a Uri. - public static IImageList makeImageList(ContentResolver cr, Uri uri, - int sort) { - String uriString = (uri != null) ? uri.toString() : ""; - - if (uriString.startsWith("content://media/external/video")) { - return makeImageList(cr, DataLocation.EXTERNAL, INCLUDE_VIDEOS, - sort, null); - } else if (isSingleImageMode(uriString)) { - return makeSingleImageList(cr, uri); - } else { - String bucketId = uri.getQueryParameter("bucketId"); - return makeImageList(cr, DataLocation.ALL, INCLUDE_IMAGES, sort, - bucketId); - } - } - - static boolean isSingleImageMode(String uriString) { - return !uriString.startsWith( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString()) - && !uriString.startsWith( - MediaStore.Images.Media.INTERNAL_CONTENT_URI.toString()); - } - private static class EmptyImageList implements IImageList { public void close() { } @@ -407,22 +339,6 @@ public class ImageManager { public IImage getImageAt(int i) { return null; } - - public IImage getImageForUri(Uri uri) { - return null; - } - - public boolean removeImage(IImage image) { - return false; - } - - public boolean removeImageAt(int i) { - return false; - } - - public int getImageIndex(IImage image) { - throw new UnsupportedOperationException(); - } } public static ImageListParam getImageListParam(DataLocation location, @@ -435,12 +351,6 @@ public class ImageManager { return param; } - public static ImageListParam getSingleImageListParam(Uri uri) { - ImageListParam param = new ImageListParam(); - param.mSingleImageUri = uri; - return param; - } - public static IImageList makeImageList(ContentResolver cr, DataLocation location, int inclusion, int sort, String bucketId) { ImageListParam param = getImageListParam(location, inclusion, sort, @@ -448,10 +358,6 @@ public class ImageManager { return makeImageList(cr, param); } - public static IImageList makeSingleImageList(ContentResolver cr, Uri uri) { - return makeImageList(cr, getSingleImageListParam(uri)); - } - private static boolean checkFsWritable() { // Create a temporary file to see whether a volume is really writeable. // It's important not to put it in the root directory which may have a diff --git a/src/com/android/camera/ImageViewTouchBase.java b/src/com/android/camera/ImageViewTouchBase.java deleted file mode 100644 index 5a9069a..0000000 --- a/src/com/android/camera/ImageViewTouchBase.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Matrix; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.widget.ImageView; - -abstract class ImageViewTouchBase extends ImageView { - - @SuppressWarnings("unused") - private static final String TAG = "ImageViewTouchBase"; - - // This is the base transformation which is used to show the image - // initially. The current computation for this shows the image in - // it's entirety, letterboxing as needed. One could choose to - // show the image as cropped instead. - // - // This matrix is recomputed when we go from the thumbnail image to - // the full size image. - protected Matrix mBaseMatrix = new Matrix(); - - // This is the supplementary transformation which reflects what - // the user has done in terms of zooming and panning. - // - // This matrix remains the same when we go from the thumbnail image - // to the full size image. - protected Matrix mSuppMatrix = new Matrix(); - - // This is the final matrix which is computed as the concatentation - // of the base matrix and the supplementary matrix. - private final Matrix mDisplayMatrix = new Matrix(); - - // Temporary buffer used for getting the values out of a matrix. - private final float[] mMatrixValues = new float[9]; - - // The current bitmap being displayed. - protected final RotateBitmap mBitmapDisplayed = new RotateBitmap(null); - - int mThisWidth = -1, mThisHeight = -1; - - float mMaxZoom; - - // ImageViewTouchBase will pass a Bitmap to the Recycler if it has finished - // its use of that Bitmap. - public interface Recycler { - public void recycle(Bitmap b); - } - - public void setRecycler(Recycler r) { - mRecycler = r; - } - - private Recycler mRecycler; - - @Override - protected void onLayout(boolean changed, int left, int top, - int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mThisWidth = right - left; - mThisHeight = bottom - top; - Runnable r = mOnLayoutRunnable; - if (r != null) { - mOnLayoutRunnable = null; - r.run(); - } - if (mBitmapDisplayed.getBitmap() != null) { - getProperBaseMatrix(mBitmapDisplayed, mBaseMatrix); - setImageMatrix(getImageViewMatrix()); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK - && event.getRepeatCount() == 0) { - event.startTracking(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() - && !event.isCanceled()) { - if (getScale() > 1.0f) { - // If we're zoomed in, pressing Back jumps out to show the - // entire image, otherwise Back returns the user to the gallery. - zoomTo(1.0f); - return true; - } - } - return super.onKeyUp(keyCode, event); - } - - @Override - public void setImageBitmap(Bitmap bitmap) { - setImageBitmap(bitmap, 0); - } - - private void setImageBitmap(Bitmap bitmap, int rotation) { - super.setImageBitmap(bitmap); - Drawable d = getDrawable(); - if (d != null) { - d.setDither(true); - } - - Bitmap old = mBitmapDisplayed.getBitmap(); - mBitmapDisplayed.setBitmap(bitmap); - mBitmapDisplayed.setRotation(rotation); - - if (old != null && old != bitmap && mRecycler != null) { - mRecycler.recycle(old); - } - } - - public void clear() { - setImageBitmapResetBase(null, true); - } - - private Runnable mOnLayoutRunnable = null; - - // This function changes bitmap, reset base matrix according to the size - // of the bitmap, and optionally reset the supplementary matrix. - public void setImageBitmapResetBase(final Bitmap bitmap, - final boolean resetSupp) { - setImageRotateBitmapResetBase(new RotateBitmap(bitmap), resetSupp); - } - - public void setImageRotateBitmapResetBase(final RotateBitmap bitmap, - final boolean resetSupp) { - final int viewWidth = getWidth(); - - if (viewWidth <= 0) { - mOnLayoutRunnable = new Runnable() { - public void run() { - setImageRotateBitmapResetBase(bitmap, resetSupp); - } - }; - return; - } - - if (bitmap.getBitmap() != null) { - getProperBaseMatrix(bitmap, mBaseMatrix); - setImageBitmap(bitmap.getBitmap(), bitmap.getRotation()); - } else { - mBaseMatrix.reset(); - setImageBitmap(null); - } - - if (resetSupp) { - mSuppMatrix.reset(); - } - setImageMatrix(getImageViewMatrix()); - mMaxZoom = maxZoom(); - } - - // Center as much as possible in one or both axis. Centering is - // defined as follows: if the image is scaled down below the - // view's dimensions then center it (literally). If the image - // is scaled larger than the view and is translated out of view - // then translate it back into view (i.e. eliminate black bars). - protected void center(boolean horizontal, boolean vertical) { - if (mBitmapDisplayed.getBitmap() == null) { - return; - } - - Matrix m = getImageViewMatrix(); - - RectF rect = new RectF(0, 0, - mBitmapDisplayed.getBitmap().getWidth(), - mBitmapDisplayed.getBitmap().getHeight()); - - m.mapRect(rect); - - float height = rect.height(); - float width = rect.width(); - - float deltaX = 0, deltaY = 0; - - if (vertical) { - int viewHeight = getHeight(); - if (height < viewHeight) { - deltaY = (viewHeight - height) / 2 - rect.top; - } else if (rect.top > 0) { - deltaY = -rect.top; - } else if (rect.bottom < viewHeight) { - deltaY = getHeight() - rect.bottom; - } - } - - if (horizontal) { - int viewWidth = getWidth(); - if (width < viewWidth) { - deltaX = (viewWidth - width) / 2 - rect.left; - } else if (rect.left > 0) { - deltaX = -rect.left; - } else if (rect.right < viewWidth) { - deltaX = viewWidth - rect.right; - } - } - - postTranslate(deltaX, deltaY); - setImageMatrix(getImageViewMatrix()); - } - - public ImageViewTouchBase(Context context) { - super(context); - init(); - } - - public ImageViewTouchBase(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - private void init() { - setScaleType(ImageView.ScaleType.MATRIX); - } - - protected float getValue(Matrix matrix, int whichValue) { - matrix.getValues(mMatrixValues); - return mMatrixValues[whichValue]; - } - - // Get the scale factor out of the matrix. - protected float getScale(Matrix matrix) { - return getValue(matrix, Matrix.MSCALE_X); - } - - protected float getScale() { - return getScale(mSuppMatrix); - } - - // Setup the base matrix so that the image is centered and scaled properly. - private void getProperBaseMatrix(RotateBitmap bitmap, Matrix matrix) { - float viewWidth = getWidth(); - float viewHeight = getHeight(); - - float w = bitmap.getWidth(); - float h = bitmap.getHeight(); - matrix.reset(); - - // We limit up-scaling to 3x otherwise the result may look bad if it's - // a small icon. - float widthScale = Math.min(viewWidth / w, 3.0f); - float heightScale = Math.min(viewHeight / h, 3.0f); - float scale = Math.min(widthScale, heightScale); - - matrix.postConcat(bitmap.getRotateMatrix()); - matrix.postScale(scale, scale); - - matrix.postTranslate( - (viewWidth - w * scale) / 2F, - (viewHeight - h * scale) / 2F); - } - - // Combine the base matrix and the supp matrix to make the final matrix. - protected Matrix getImageViewMatrix() { - // The final matrix is computed as the concatentation of the base matrix - // and the supplementary matrix. - mDisplayMatrix.set(mBaseMatrix); - mDisplayMatrix.postConcat(mSuppMatrix); - return mDisplayMatrix; - } - - static final float SCALE_RATE = 1.25F; - - // Sets the maximum zoom, which is a scale relative to the base matrix. It - // is calculated to show the image at 400% zoom regardless of screen or - // image orientation. If in the future we decode the full 3 megapixel image, - // rather than the current 1024x768, this should be changed down to 200%. - protected float maxZoom() { - if (mBitmapDisplayed.getBitmap() == null) { - return 1F; - } - - float fw = (float) mBitmapDisplayed.getWidth() / (float) mThisWidth; - float fh = (float) mBitmapDisplayed.getHeight() / (float) mThisHeight; - float max = Math.max(fw, fh) * 4; - return max; - } - - protected void zoomTo(float scale, float centerX, float centerY) { - if (scale > mMaxZoom) { - scale = mMaxZoom; - } - - float oldScale = getScale(); - float deltaScale = scale / oldScale; - - mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY); - setImageMatrix(getImageViewMatrix()); - center(true, true); - } - - protected void zoomTo(float scale) { - float cx = getWidth() / 2F; - float cy = getHeight() / 2F; - - zoomTo(scale, cx, cy); - } - - protected void zoomToPoint(float scale, float pointX, float pointY) { - float cx = getWidth() / 2F; - float cy = getHeight() / 2F; - - panBy(cx - pointX, cy - pointY); - zoomTo(scale, cx, cy); - } - - protected void zoomIn() { - zoomIn(SCALE_RATE); - } - - protected void zoomOut() { - zoomOut(SCALE_RATE); - } - - protected void zoomIn(float rate) { - if (getScale() >= mMaxZoom) { - return; // Don't let the user zoom into the molecular level. - } - if (mBitmapDisplayed.getBitmap() == null) { - return; - } - - float cx = getWidth() / 2F; - float cy = getHeight() / 2F; - - mSuppMatrix.postScale(rate, rate, cx, cy); - setImageMatrix(getImageViewMatrix()); - } - - protected void zoomOut(float rate) { - if (mBitmapDisplayed.getBitmap() == null) { - return; - } - - float cx = getWidth() / 2F; - float cy = getHeight() / 2F; - - // Zoom out to at most 1x. - Matrix tmp = new Matrix(mSuppMatrix); - tmp.postScale(1F / rate, 1F / rate, cx, cy); - - if (getScale(tmp) < 1F) { - mSuppMatrix.setScale(1F, 1F, cx, cy); - } else { - mSuppMatrix.postScale(1F / rate, 1F / rate, cx, cy); - } - setImageMatrix(getImageViewMatrix()); - center(true, true); - } - - protected void postTranslate(float dx, float dy) { - mSuppMatrix.postTranslate(dx, dy); - } - - protected void panBy(float dx, float dy) { - postTranslate(dx, dy); - setImageMatrix(getImageViewMatrix()); - } -} diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java index 822df8c..8e6fb22 100644 --- a/src/com/android/camera/MenuHelper.java +++ b/src/com/android/camera/MenuHelper.java @@ -23,35 +23,14 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.DialogInterface.OnClickListener; -import android.location.Geocoder; -import android.media.ExifInterface; import android.net.Uri; import android.os.Environment; -import android.os.Handler; import android.os.StatFs; -import android.preference.PreferenceManager; import android.provider.MediaStore; import android.provider.MediaStore.Images; -import android.text.format.Formatter; import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.camera.gallery.IImage; import java.io.Closeable; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; /** * A utility class to handle various kinds of menu operations. @@ -102,20 +81,6 @@ public class MenuHelper { private static final int NO_ANIMATION = 0; - public interface MenuItemsResult { - public void gettingReadyToOpen(Menu menu, IImage image); - public void aboutToCall(MenuItem item, IImage image); - } - - public interface MenuInvoker { - public void run(MenuCallback r); - } - - /** A callback to be invoked when a menu item is clicked. */ - public interface MenuCallback { - public void run(Uri uri, IImage image); - } - public static void closeSilently(Closeable c) { if (c != null) { try { @@ -126,566 +91,6 @@ public class MenuHelper { } } - public static long getImageFileSize(IImage image) { - java.io.InputStream data = image.fullSizeImageData(); - if (data == null) return -1; - try { - return data.available(); - } catch (java.io.IOException ex) { - return -1; - } finally { - closeSilently(data); - } - } - - // This is a hack before we find a solution to pass a permission to other - // applications. See bug #1735149, #1836138. - // Checks if the URI is on our whitelist: - // content://media/... (MediaProvider) - // file:///sdcard/... (Browser download) - public static boolean isWhiteListUri(Uri uri) { - if (uri == null) return false; - - String scheme = uri.getScheme(); - String authority = uri.getAuthority(); - - if (scheme.equals("content") && authority.equals("media")) { - return true; - } - - if (scheme.equals("file")) { - List p = uri.getPathSegments(); - - if (p.size() >= 1 && p.get(0).equals("sdcard")) { - return true; - } - } - - return false; - } - - public static void enableShareMenuItem(Menu menu, boolean enabled) { - MenuItem item = menu.findItem(MENU_IMAGE_SHARE); - if (item != null) { - item.setVisible(enabled); - item.setEnabled(enabled); - } - } - - public static boolean hasLatLngData(IImage image) { - ExifInterface exif = getExif(image); - if (exif == null) return false; - float latlng[] = new float[2]; - return exif.getLatLong(latlng); - } - - public static void enableShowOnMapMenuItem(Menu menu, boolean enabled) { - MenuItem item = menu.findItem(MENU_IMAGE_SHOWMAP); - if (item != null) { - item.setEnabled(enabled); - } - } - - private static void setDetailsValue(View d, String text, int valueId) { - ((TextView) d.findViewById(valueId)).setText(text); - } - - private static void hideDetailsRow(View d, int rowId) { - d.findViewById(rowId).setVisibility(View.GONE); - } - - private static class UpdateLocationCallback implements - ReverseGeocoderTask.Callback { - WeakReference mView; - - public UpdateLocationCallback(WeakReference view) { - mView = view; - } - - public void onComplete(String location) { - // View d is per-thread data, so when setDetailsValue is - // executed by UI thread, it doesn't matter whether the - // details dialog is dismissed or not. - View view = mView.get(); - if (view == null) return; - if (!location.equals(MenuHelper.EMPTY_STRING)) { - MenuHelper.setDetailsValue(view, location, - R.id.details_location_value); - } else { - MenuHelper.hideDetailsRow(view, R.id.details_location_row); - } - } - } - - private static void setLatLngDetails(final View d, Activity context, - ExifInterface exif) { - float[] latlng = new float[2]; - if (exif.getLatLong(latlng)) { - setDetailsValue(d, String.valueOf(latlng[0]), - R.id.details_latitude_value); - setDetailsValue(d, String.valueOf(latlng[1]), - R.id.details_longitude_value); - - if (latlng[0] == INVALID_LATLNG || latlng[1] == INVALID_LATLNG) { - hideDetailsRow(d, R.id.details_latitude_row); - hideDetailsRow(d, R.id.details_longitude_row); - hideDetailsRow(d, R.id.details_location_row); - return; - } - - UpdateLocationCallback cb = new UpdateLocationCallback( - new WeakReference(d)); - Geocoder geocoder = new Geocoder(context); - new ReverseGeocoderTask(geocoder, latlng, cb).execute(); - } else { - hideDetailsRow(d, R.id.details_latitude_row); - hideDetailsRow(d, R.id.details_longitude_row); - hideDetailsRow(d, R.id.details_location_row); - } - } - - private static ExifInterface getExif(IImage image) { - if (!JPEG_MIME_TYPE.equals(image.getMimeType())) { - return null; - } - - try { - return new ExifInterface(image.getDataPath()); - } catch (IOException ex) { - Log.e(TAG, "cannot read exif", ex); - return null; - } - } - // Called when "Show on Maps" is clicked. - // Displays image location on Google Maps for further operations. - private static boolean onShowMapClicked(MenuInvoker onInvoke, - final Handler handler, - final Activity activity) { - onInvoke.run(new MenuCallback() { - public void run(Uri u, IImage image) { - if (image == null) { - return; - } - - boolean ok = false; - ExifInterface exif = getExif(image); - float latlng[] = null; - if (exif != null) { - latlng = new float[2]; - if (exif.getLatLong(latlng)) { - ok = true; - } - } - - if (!ok) { - handler.post(new Runnable() { - public void run() { - Toast.makeText(activity, - R.string.no_location_image, - Toast.LENGTH_SHORT).show(); - } - }); - return; - } - - // Can't use geo:latitude,longitude because it only centers - // the MapView to specified location, but we need a bubble - // for further operations (routing to/from). - // The q=(lat, lng) syntax is suggested by geo-team. - String uri = "http://maps.google.com/maps?f=q&" + - "q=(" + latlng[0] + "," + latlng[1] + ")"; - activity.startActivity(new Intent( - android.content.Intent.ACTION_VIEW, - Uri.parse(uri))); - } - }); - return true; - } - - private static void hideExifInformation(View d) { - hideDetailsRow(d, R.id.details_resolution_row); - hideDetailsRow(d, R.id.details_make_row); - hideDetailsRow(d, R.id.details_model_row); - hideDetailsRow(d, R.id.details_whitebalance_row); - hideDetailsRow(d, R.id.details_latitude_row); - hideDetailsRow(d, R.id.details_longitude_row); - hideDetailsRow(d, R.id.details_location_row); - } - - private static void showExifInformation(IImage image, View d, - Activity activity) { - ExifInterface exif = getExif(image); - if (exif == null) { - hideExifInformation(d); - return; - } - - String value = exif.getAttribute(ExifInterface.TAG_MAKE); - if (value != null) { - setDetailsValue(d, value, R.id.details_make_value); - } else { - hideDetailsRow(d, R.id.details_make_row); - } - - value = exif.getAttribute(ExifInterface.TAG_MODEL); - if (value != null) { - setDetailsValue(d, value, R.id.details_model_value); - } else { - hideDetailsRow(d, R.id.details_model_row); - } - - value = getWhiteBalanceString(exif); - if (value != null && !value.equals(EMPTY_STRING)) { - setDetailsValue(d, value, R.id.details_whitebalance_value); - } else { - hideDetailsRow(d, R.id.details_whitebalance_row); - } - - setLatLngDetails(d, activity, exif); - } - - /** - * Returns a human-readable string describing the white balance value. - * Returns empty string if there is no white balance value or it is not - * recognized. - */ - private static String getWhiteBalanceString(ExifInterface exif) { - int whitebalance = exif.getAttributeInt( - ExifInterface.TAG_WHITE_BALANCE, -1); - if (whitebalance == -1) return ""; - - switch (whitebalance) { - case ExifInterface.WHITEBALANCE_AUTO: - return "Auto"; - case ExifInterface.WHITEBALANCE_MANUAL: - return "Manual"; - default: - return ""; - } - } - - // Called when "Details" is clicked. - // Displays detailed information about the image/video. - private static boolean onDetailsClicked(MenuInvoker onInvoke, - final Handler handler, - final Activity activity) { - return true; - } - - // Called when "Rotate left" or "Rotate right" is clicked. - private static boolean onRotateClicked(MenuInvoker onInvoke, - final int degree) { - onInvoke.run(new MenuCallback() { - public void run(Uri u, IImage image) { - if (image == null || image.isReadonly()) { - return; - } - image.rotateImageBy(degree); - } - }); - return true; - } - - // Called when "Crop" is clicked. - private static boolean onCropClicked(MenuInvoker onInvoke, - final Activity activity) { - onInvoke.run(new MenuCallback() { - public void run(Uri u, IImage image) { - if (u == null) { - return; - } - - Intent cropIntent = new Intent( - "com.android.camera.action.CROP"); - cropIntent.setData(u); - activity.startActivityForResult( - cropIntent, RESULT_COMMON_MENU_CROP); - } - }); - return true; - } - - // Called when "Set as" is clicked. - private static boolean onSetAsClicked(MenuInvoker onInvoke, - final Activity activity) { - onInvoke.run(new MenuCallback() { - public void run(Uri u, IImage image) { - if (u == null || image == null) { - return; - } - - Intent intent = Util.createSetAsIntent(image); - activity.startActivity(Intent.createChooser(intent, - activity.getText(R.string.setImage))); - } - }); - return true; - } - - // Called when "Share" is clicked. - private static boolean onImageShareClicked(MenuInvoker onInvoke, - final Activity activity) { - onInvoke.run(new MenuCallback() { - public void run(Uri u, IImage image) { - if (image == null) return; - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - String mimeType = image.getMimeType(); - intent.setType(mimeType); - intent.putExtra(Intent.EXTRA_STREAM, u); - boolean isImage = ImageManager.isImage(image); - try { - activity.startActivity(Intent.createChooser(intent, - activity.getText(isImage - ? R.string.sendImage - : R.string.sendVideo))); - } catch (android.content.ActivityNotFoundException ex) { - Toast.makeText(activity, isImage - ? R.string.no_way_to_share_image - : R.string.no_way_to_share_video, - Toast.LENGTH_SHORT).show(); - } - } - }); - return true; - } - - // Called when "Play" is clicked. - private static boolean onViewPlayClicked(MenuInvoker onInvoke, - final Activity activity) { - onInvoke.run(new MenuCallback() { - public void run(Uri uri, IImage image) { - if (image != null) { - Intent intent = new Intent(Intent.ACTION_VIEW, - image.fullSizeImageUri()); - activity.startActivity(intent); - } - }}); - return true; - } - - // Called when "Delete" is clicked. - private static boolean onDeleteClicked(MenuInvoker onInvoke, - final Activity activity, final Runnable onDelete) { - onInvoke.run(new MenuCallback() { - public void run(Uri uri, IImage image) { - if (image != null) { - deleteImage(activity, onDelete, image); - } - }}); - return true; - } - - static MenuItemsResult addImageMenuItems( - Menu menu, - int inclusions, - final Activity activity, - final Handler handler, - final Runnable onDelete, - final MenuInvoker onInvoke) { - final ArrayList requiresWriteAccessItems = - new ArrayList(); - final ArrayList requiresNoDrmAccessItems = - new ArrayList(); - final ArrayList requiresImageItems = - new ArrayList(); - final ArrayList requiresVideoItems = - new ArrayList(); - - if ((inclusions & INCLUDE_ROTATE_MENU) != 0) { - SubMenu rotateSubmenu = menu.addSubMenu(Menu.NONE, Menu.NONE, - POSITION_IMAGE_ROTATE, R.string.rotate) - .setIcon(android.R.drawable.ic_menu_rotate); - // Don't show the rotate submenu if the item at hand is read only - // since the items within the submenu won't be shown anyway. This - // is really a framework bug in that it shouldn't show the submenu - // if the submenu has no visible items. - MenuItem rotateLeft = rotateSubmenu.add(R.string.rotate_left) - .setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onRotateClicked(onInvoke, -90); - } - }).setAlphabeticShortcut('l'); - - MenuItem rotateRight = rotateSubmenu.add(R.string.rotate_right) - .setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onRotateClicked(onInvoke, 90); - } - }).setAlphabeticShortcut('r'); - - requiresWriteAccessItems.add(rotateSubmenu.getItem()); - requiresWriteAccessItems.add(rotateLeft); - requiresWriteAccessItems.add(rotateRight); - - requiresImageItems.add(rotateSubmenu.getItem()); - requiresImageItems.add(rotateLeft); - requiresImageItems.add(rotateRight); - } - - if ((inclusions & INCLUDE_CROP_MENU) != 0) { - MenuItem autoCrop = menu.add(Menu.NONE, Menu.NONE, - POSITION_IMAGE_CROP, R.string.camera_crop); - autoCrop.setIcon(android.R.drawable.ic_menu_crop); - autoCrop.setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onCropClicked(onInvoke, activity); - } - }); - requiresWriteAccessItems.add(autoCrop); - requiresImageItems.add(autoCrop); - } - - if ((inclusions & INCLUDE_SET_MENU) != 0) { - MenuItem setMenu = menu.add(Menu.NONE, Menu.NONE, - POSITION_IMAGE_SET, R.string.camera_set); - setMenu.setIcon(android.R.drawable.ic_menu_set_as); - setMenu.setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onSetAsClicked(onInvoke, activity); - } - }); - requiresImageItems.add(setMenu); - } - - if ((inclusions & INCLUDE_SHARE_MENU) != 0) { - MenuItem item1 = menu.add(Menu.NONE, MENU_IMAGE_SHARE, - POSITION_IMAGE_SHARE, R.string.camera_share) - .setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onImageShareClicked(onInvoke, activity); - } - }); - item1.setIcon(android.R.drawable.ic_menu_share); - MenuItem item = item1; - requiresNoDrmAccessItems.add(item); - } - - if ((inclusions & INCLUDE_DELETE_MENU) != 0) { - MenuItem deleteItem = menu.add(Menu.NONE, Menu.NONE, - POSITION_IMAGE_TOSS, R.string.camera_toss); - requiresWriteAccessItems.add(deleteItem); - deleteItem.setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onDeleteClicked(onInvoke, activity, - onDelete); - } - }) - .setAlphabeticShortcut('d') - .setIcon(android.R.drawable.ic_menu_delete); - } - - if ((inclusions & INCLUDE_DETAILS_MENU) != 0) { - MenuItem detailsMenu = menu.add(Menu.NONE, Menu.NONE, - POSITION_DETAILS, R.string.details) - .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onDetailsClicked(onInvoke, handler, activity); - } - }); - detailsMenu.setIcon(R.drawable.ic_menu_view_details); - } - - if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) { - MenuItem showOnMapItem = menu.add(Menu.NONE, MENU_IMAGE_SHOWMAP, - POSITION_SHOWMAP, R.string.show_on_map); - showOnMapItem.setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onShowMapClicked(onInvoke, - handler, activity); - } - }).setIcon(R.drawable.ic_menu_3d_globe); - requiresImageItems.add(showOnMapItem); - } - - if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) { - MenuItem videoPlayItem = menu.add(Menu.NONE, Menu.NONE, - POSITION_VIEWPLAY, R.string.video_play) - .setOnMenuItemClickListener( - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - return onViewPlayClicked(onInvoke, activity); - } - }); - videoPlayItem.setIcon(R.drawable.ic_menu_play_clip); - requiresVideoItems.add(videoPlayItem); - } - - return new MenuItemsResult() { - public void gettingReadyToOpen(Menu menu, IImage image) { - // protect against null here. this isn't strictly speaking - // required but if a client app isn't handling sdcard removal - // properly it could happen - if (image == null) { - return; - } - - ArrayList enableList = new ArrayList(); - ArrayList disableList = new ArrayList(); - ArrayList list; - - list = image.isReadonly() ? disableList : enableList; - list.addAll(requiresWriteAccessItems); - - list = image.isDrm() ? disableList : enableList; - list.addAll(requiresNoDrmAccessItems); - - list = ImageManager.isImage(image) ? enableList : disableList; - list.addAll(requiresImageItems); - - list = ImageManager.isVideo(image) ? enableList : disableList; - list.addAll(requiresVideoItems); - - for (MenuItem item : enableList) { - item.setVisible(true); - item.setEnabled(true); - } - - for (MenuItem item : disableList) { - item.setVisible(false); - item.setEnabled(false); - } - } - - // must override abstract method - public void aboutToCall(MenuItem menu, IImage image) { - } - }; - } - - static void deletePhoto(Activity activity, Runnable onDelete) { - deleteImpl(activity, onDelete, true); - } - - static void deleteImage( - Activity activity, Runnable onDelete, IImage image) { - deleteImpl(activity, onDelete, ImageManager.isImage(image)); - } - - static void deleteImpl( - Activity activity, Runnable onDelete, boolean isImage) { - boolean needConfirm = PreferenceManager - .getDefaultSharedPreferences(activity) - .getBoolean("pref_gallery_confirm_delete_key", true); - if (!needConfirm) { - if (onDelete != null) onDelete.run(); - } else { - String title = activity.getString(R.string.confirm_delete_title); - String message = activity.getString(isImage - ? R.string.confirm_delete_message - : R.string.confirm_delete_video_message); - confirmAction(activity, title, message, onDelete); - } - } - public static void confirmAction(Context context, String title, String message, final Runnable action) { OnClickListener listener = new OnClickListener() { @@ -755,23 +160,6 @@ public class MenuHelper { } } - public static String formatDuration(final Context context, - int durationMs) { - int duration = durationMs / 1000; - int h = duration / 3600; - int m = (duration - h * 3600) / 60; - int s = duration - (h * 3600 + m * 60); - String durationValue; - if (h == 0) { - durationValue = String.format( - context.getString(R.string.details_ms), m, s); - } else { - durationValue = String.format( - context.getString(R.string.details_hms), h, m, s); - } - return durationValue; - } - public static int calculatePicturesRemaining() { try { if (!ImageManager.hasStorage()) { diff --git a/src/com/android/camera/ReverseGeocoderTask.java b/src/com/android/camera/ReverseGeocoderTask.java deleted file mode 100644 index f4e7c71..0000000 --- a/src/com/android/camera/ReverseGeocoderTask.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.location.Address; -import android.location.Geocoder; -import android.os.AsyncTask; -import android.util.Log; - -import java.io.IOException; -import java.util.List; - -/** - * A asynchronous task which does reverse geocoding. - * - *

Because it may take a long time to return, we put it in an AsyncTask. - * The input is latitude and longitude, and the output is a descriptive string - * for the specified location. The result is passed to a callback. - */ -public class ReverseGeocoderTask extends AsyncTask { - private static final String TAG = "ReverseGeocoder"; - - /** - * A callback to be invoked when the reverse geocoding task is done. - */ - public static interface Callback { - public void onComplete(String location); - } - - private Geocoder mGeocoder; - private float mLat; - private float mLng; - private Callback mCallback; - - public ReverseGeocoderTask(Geocoder geocoder, float[] latlng, - Callback callback) { - mGeocoder = geocoder; - mLat = latlng[0]; - mLng = latlng[1]; - mCallback = callback; - } - - @Override - protected String doInBackground(Void... params) { - String value = MenuHelper.EMPTY_STRING; - try { - List

address = mGeocoder.getFromLocation(mLat, mLng, 1); - StringBuilder sb = new StringBuilder(); - for (Address addr : address) { - int index = addr.getMaxAddressLineIndex(); - sb.append(addr.getAddressLine(index)); - } - value = sb.toString(); - } catch (IOException ex) { - value = MenuHelper.EMPTY_STRING; - Log.e(TAG, "Geocoder exception: ", ex); - } catch (RuntimeException ex) { - value = MenuHelper.EMPTY_STRING; - Log.e(TAG, "Geocoder exception: ", ex); - } - return value; - } - - @Override - protected void onPostExecute(String location) { - mCallback.onComplete(location); - } -} diff --git a/src/com/android/camera/RotateBitmap.java b/src/com/android/camera/RotateBitmap.java deleted file mode 100644 index 635ec08..0000000 --- a/src/com/android/camera/RotateBitmap.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.graphics.Bitmap; -import android.graphics.Matrix; - -/** - * This class represents a bitmap with a rotation (The rotation can only be - * 0, 90, 180, 270 degrees). - * - * Because it takes twice the memory to do the rotation (the original bitmap - * and the new bitmap), we do not actually rotate the bitmap. We pass the - * rotation along with the bitmap in the program and only apply the rotation - * when we are actually drawing the bitmap. - */ -public class RotateBitmap { - private static final String TAG = "RotateBitmap"; - private Bitmap mBitmap; - private int mRotation; - - public RotateBitmap(Bitmap bitmap) { - mBitmap = bitmap; - mRotation = 0; - } - - public RotateBitmap(Bitmap bitmap, int rotation) { - mBitmap = bitmap; - mRotation = rotation % 360; - } - - public void setRotation(int rotation) { - mRotation = rotation; - } - - public int getRotation() { - return mRotation; - } - - public Bitmap getBitmap() { - return mBitmap; - } - - public void setBitmap(Bitmap bitmap) { - mBitmap = bitmap; - } - - public Matrix getRotateMatrix() { - // By default this is an identity matrix. - Matrix matrix = new Matrix(); - if (mRotation != 0) { - // We want to do the rotation at origin, but since the bounding - // rectangle will be changed after rotation, so the delta values - // are based on old & new width/height respectively. - int cx = mBitmap.getWidth() / 2; - int cy = mBitmap.getHeight() / 2; - matrix.preTranslate(-cx, -cy); - matrix.postRotate(mRotation); - matrix.postTranslate(getWidth() / 2, getHeight() / 2); - } - return matrix; - } - - public boolean isOrientationChanged() { - return (mRotation / 90) % 2 != 0; - } - - public int getHeight() { - if (isOrientationChanged()) { - return mBitmap.getWidth(); - } else { - return mBitmap.getHeight(); - } - } - - public int getWidth() { - if (isOrientationChanged()) { - return mBitmap.getHeight(); - } else { - return mBitmap.getWidth(); - } - } - - public void recycle() { - if (mBitmap != null) { - mBitmap.recycle(); - mBitmap = null; - } - } -} - diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java index 5cbfd13..2d28007 100644 --- a/src/com/android/camera/Util.java +++ b/src/com/android/camera/Util.java @@ -18,14 +18,10 @@ package com.android.camera; import android.app.Activity; import android.app.AlertDialog; -import android.content.ContentResolver; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; -import android.net.Uri; -import android.os.ParcelFileDescriptor; import android.util.Log; import android.view.View; import android.view.animation.Animation; @@ -34,8 +30,6 @@ import android.view.animation.TranslateAnimation; import com.android.camera.gallery.IImage; import java.io.Closeable; -import java.io.FileDescriptor; -import java.io.IOException; /** * Collection of utility functions used in this package. @@ -152,70 +146,6 @@ public class Util { } } - public static void closeSilently(ParcelFileDescriptor c) { - if (c == null) return; - try { - c.close(); - } catch (Throwable t) { - // do nothing - } - } - - /** - * Make a bitmap from a given Uri. - * - * @param uri - */ - public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, - Uri uri, ContentResolver cr) { - ParcelFileDescriptor input = null; - try { - input = cr.openFileDescriptor(uri, "r"); - return makeBitmap(minSideLength, maxNumOfPixels, uri, cr, input, - null); - } catch (IOException ex) { - return null; - } finally { - closeSilently(input); - } - } - - public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, - ParcelFileDescriptor pfd) { - return makeBitmap(minSideLength, maxNumOfPixels, null, null, pfd, - null); - } - - public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, - Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, - BitmapFactory.Options options) { - try { - if (pfd == null) pfd = makeInputStream(uri, cr); - if (pfd == null) return null; - if (options == null) options = new BitmapFactory.Options(); - - FileDescriptor fd = pfd.getFileDescriptor(); - options.inJustDecodeBounds = true; - BitmapManager.instance().decodeFileDescriptor(fd, options); - if (options.mCancel || options.outWidth == -1 - || options.outHeight == -1) { - return null; - } - options.inSampleSize = computeSampleSize( - options, minSideLength, maxNumOfPixels); - options.inJustDecodeBounds = false; - - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - return BitmapManager.instance().decodeFileDescriptor(fd, options); - } catch (OutOfMemoryError ex) { - Log.e(TAG, "Got oom exception ", ex); - return null; - } finally { - closeSilently(pfd); - } - } - public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { try { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -240,30 +170,12 @@ public class Util { } } - private static ParcelFileDescriptor makeInputStream( - Uri uri, ContentResolver cr) { - try { - return cr.openFileDescriptor(uri, "r"); - } catch (IOException ex) { - return null; - } - } - public static void Assert(boolean cond) { if (!cond) { throw new AssertionError(); } } - // Returns an intent which is used for "set as" menu items. - public static Intent createSetAsIntent(IImage image) { - Uri u = image.fullSizeImageUri(); - Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); - intent.setDataAndType(u, image.getMimeType()); - intent.putExtra("mimeType", image.getMimeType()); - return intent; - } - public static void showFatalErrorAndFinish( final Activity activity, String title, String message) { DialogInterface.OnClickListener buttonListener = diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java index 311fde3..0ffe58c 100644 --- a/src/com/android/camera/VideoCamera.java +++ b/src/com/android/camera/VideoCamera.java @@ -33,7 +33,6 @@ import android.hardware.Camera.Size; import android.media.MediaRecorder; import android.media.ThumbnailUtils; import android.media.CamcorderProfile; -import android.media.CamcorderProfile.Quality; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -74,7 +73,6 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; /** @@ -1558,22 +1556,3 @@ public class VideoCamera extends NoSearchActivity } } - -// -// DefaultHashMap is a HashMap which returns a default value if the specified -// key is not found. -// -@SuppressWarnings("serial") -class DefaultHashMap extends HashMap { - private V mDefaultValue; - - public void putDefault(V defaultValue) { - mDefaultValue = defaultValue; - } - - @Override - public V get(Object key) { - V value = super.get(key); - return (value == null) ? mDefaultValue : value; - } -} diff --git a/src/com/android/camera/gallery/BaseImage.java b/src/com/android/camera/gallery/BaseImage.java index bbd45f1..6ee0e76 100644 --- a/src/com/android/camera/gallery/BaseImage.java +++ b/src/com/android/camera/gallery/BaseImage.java @@ -44,33 +44,15 @@ public abstract class BaseImage implements IImage { // Database field protected Uri mUri; protected long mId; - protected String mDataPath; - protected final int mIndex; - protected String mMimeType; private final long mDateTaken; - private String mTitle; - protected BaseImageList mContainer; - - private int mWidth = UNKNOWN_LENGTH; - private int mHeight = UNKNOWN_LENGTH; - - protected BaseImage(BaseImageList container, ContentResolver cr, - long id, int index, Uri uri, String dataPath, long miniThumbMagic, - String mimeType, long dateTaken, String title) { - mContainer = container; + protected BaseImage(ContentResolver cr, + long id, Uri uri, long miniThumbMagic, + long dateTaken) { mContentResolver = cr; mId = id; - mIndex = index; mUri = uri; - mDataPath = dataPath; - mMimeType = mimeType; mDateTaken = dateTaken; - mTitle = title; - } - - public String getDataPath() { - return mDataPath; } @Override @@ -84,43 +66,10 @@ public abstract class BaseImage implements IImage { return mUri.hashCode(); } - public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels) { - return fullSizeBitmap(minSideLength, maxNumberOfPixels, - IImage.ROTATE_AS_NEEDED); - } - - public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels, - boolean rotateAsNeeded) { - Uri url = mContainer.contentUri(mId); - if (url == null) return null; - - Bitmap b = Util.makeBitmap(minSideLength, maxNumberOfPixels, - url, mContentResolver); - - if (b != null && rotateAsNeeded) { - b = Util.rotate(b, getDegreesRotated()); - } - - return b; - } - - public InputStream fullSizeImageData() { - try { - InputStream input = mContentResolver.openInputStream(mUri); - return input; - } catch (IOException ex) { - return null; - } - } - public Uri fullSizeImageUri() { return mUri; } - public IImageList getContainer() { - return mContainer; - } - public long getDateTaken() { return mDateTaken; } @@ -129,42 +78,6 @@ public abstract class BaseImage implements IImage { return 0; } - public String getMimeType() { - return mMimeType; - } - - public String getTitle() { - return mTitle; - } - - private void setupDimension() { - ParcelFileDescriptor input = null; - try { - input = mContentResolver.openFileDescriptor(mUri, "r"); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapManager.instance().decodeFileDescriptor( - input.getFileDescriptor(), options); - mWidth = options.outWidth; - mHeight = options.outHeight; - } catch (FileNotFoundException ex) { - mWidth = 0; - mHeight = 0; - } finally { - Util.closeSilently(input); - } - } - - public int getWidth() { - if (mWidth == UNKNOWN_LENGTH) setupDimension(); - return mWidth; - } - - public int getHeight() { - if (mHeight == UNKNOWN_LENGTH) setupDimension(); - return mHeight; - } - public Bitmap miniThumbBitmap() { Bitmap b = null; try { @@ -181,9 +94,6 @@ public abstract class BaseImage implements IImage { return b; } - protected void onRemove() { - } - @Override public String toString() { return mUri.toString(); diff --git a/src/com/android/camera/gallery/BaseImageList.java b/src/com/android/camera/gallery/BaseImageList.java index cc42db7..97a6a11 100644 --- a/src/com/android/camera/gallery/BaseImageList.java +++ b/src/com/android/camera/gallery/BaseImageList.java @@ -130,92 +130,18 @@ public abstract class BaseImageList implements IImageList { return result; } - public boolean removeImage(IImage image) { - // TODO: need to delete the thumbnails as well - if (mContentResolver.delete(image.fullSizeImageUri(), null, null) > 0) { - ((BaseImage) image).onRemove(); - invalidateCursor(); - invalidateCache(); - return true; - } else { - return false; - } - } - - public boolean removeImageAt(int i) { - // TODO: need to delete the thumbnails as well - return removeImage(getImageAt(i)); - } - protected abstract Cursor createCursor(); protected abstract BaseImage loadImageFromCursor(Cursor cursor); - protected abstract long getImageId(Cursor cursor); - protected void invalidateCursor() { if (mCursor == null) return; mCursor.deactivate(); mCursorDeactivated = true; } - protected void invalidateCache() { - mCache.clear(); - } - private static final Pattern sPathWithId = Pattern.compile("(.*)/\\d+"); - private static String getPathWithoutId(Uri uri) { - String path = uri.getPath(); - Matcher matcher = sPathWithId.matcher(path); - return matcher.matches() ? matcher.group(1) : path; - } - - private boolean isChildImageUri(Uri uri) { - // Sometimes, the URI of an image contains a query string with key - // "bucketId" inorder to restore the image list. However, the query - // string is not part of the mBaseUri. So, we check only other parts - // of the two Uri to see if they are the same. - Uri base = mBaseUri; - return Util.equals(base.getScheme(), uri.getScheme()) - && Util.equals(base.getHost(), uri.getHost()) - && Util.equals(base.getAuthority(), uri.getAuthority()) - && Util.equals(base.getPath(), getPathWithoutId(uri)); - } - - public IImage getImageForUri(Uri uri) { - if (!isChildImageUri(uri)) return null; - // Find the id of the input URI. - long matchId; - try { - matchId = ContentUris.parseId(uri); - } catch (NumberFormatException ex) { - Log.i(TAG, "fail to get id in: " + uri, ex); - return null; - } - // TODO: design a better method to get URI of specified ID - Cursor cursor = getCursor(); - if (cursor == null) return null; - synchronized (this) { - cursor.moveToPosition(-1); // before first - for (int i = 0; cursor.moveToNext(); ++i) { - if (getImageId(cursor) == matchId) { - BaseImage image = mCache.get(i); - if (image == null) { - image = loadImageFromCursor(cursor); - mCache.put(i, image); - } - return image; - } - } - return null; - } - } - - public int getImageIndex(IImage image) { - return ((BaseImage) image).mIndex; - } - // This provides a default sorting order string for subclasses. // The list is first sorted by date, then by id. The order can be ascending // or descending, depending on the mSort variable. diff --git a/src/com/android/camera/gallery/IImage.java b/src/com/android/camera/gallery/IImage.java index 4b72de6..9fa4f4f 100644 --- a/src/com/android/camera/gallery/IImage.java +++ b/src/com/android/camera/gallery/IImage.java @@ -31,48 +31,14 @@ public interface IImage { static final int MINI_THUMB_MAX_NUM_PIXELS = 128 * 128; static final int UNCONSTRAINED = -1; - /** Get the image list which contains this image. */ - public abstract IImageList getContainer(); - - /** Get the bitmap for the full size image. */ - public abstract Bitmap fullSizeBitmap(int minSideLength, - int maxNumberOfPixels); - public abstract Bitmap fullSizeBitmap(int minSideLength, - int maxNumberOfPixels, boolean rotateAsNeeded); - public abstract int getDegreesRotated(); public static final boolean ROTATE_AS_NEEDED = true; public static final boolean NO_ROTATE = false; - /** Get the input stream associated with a given full size image. */ - public abstract InputStream fullSizeImageData(); public abstract Uri fullSizeImageUri(); - /** Get the path of the (full size) image data. */ - public abstract String getDataPath(); - - // Get the title of the image - public abstract String getTitle(); - // Get metadata of the image public abstract long getDateTaken(); - public abstract String getMimeType(); - - public abstract int getWidth(); - - public abstract int getHeight(); - - // Get property of the image - public abstract boolean isReadonly(); - public abstract boolean isDrm(); - - // Get the bitmap of the medium thumbnail - public abstract Bitmap thumbBitmap(boolean rotateAsNeeded); - // Get the bitmap of the mini thumbnail. public abstract Bitmap miniThumbBitmap(); - - // Rotate the image - public abstract boolean rotateImageBy(int degrees); - } diff --git a/src/com/android/camera/gallery/IImageList.java b/src/com/android/camera/gallery/IImageList.java index 6f3d68d..14aeb31 100644 --- a/src/com/android/camera/gallery/IImageList.java +++ b/src/com/android/camera/gallery/IImageList.java @@ -59,29 +59,6 @@ public interface IImageList { public IImage getImageAt(int i); /** - * Returns the image with a particular Uri. - * - * @param uri - * @return the image with a particular Uri. null if not found. - */ - public IImage getImageForUri(Uri uri); - - /** - * - * @param image - * @return true if the image was removed. - */ - public boolean removeImage(IImage image); - - /** - * Removes the image at the ith position. - * @param i the position - */ - public boolean removeImageAt(int i); - - public int getImageIndex(IImage image); - - /** * Closes this list to release resources, no further operation is allowed. */ public void close(); diff --git a/src/com/android/camera/gallery/Image.java b/src/com/android/camera/gallery/Image.java index df6516a..91b8dc5 100644 --- a/src/com/android/camera/gallery/Image.java +++ b/src/com/android/camera/gallery/Image.java @@ -38,16 +38,14 @@ import java.io.IOException; public class Image extends BaseImage implements IImage { private static final String TAG = "BaseImage"; - private ExifInterface mExif; - private int mRotation; - public Image(BaseImageList container, ContentResolver cr, - long id, int index, Uri uri, String dataPath, long miniThumbMagic, - String mimeType, long dateTaken, String title, + public Image(ContentResolver cr, + long id, Uri uri, long miniThumbMagic, + long dateTaken, int rotation) { - super(container, cr, id, index, uri, dataPath, miniThumbMagic, - mimeType, dateTaken, title); + super(cr, id, uri, miniThumbMagic, + dateTaken); mRotation = rotation; } @@ -55,111 +53,4 @@ public class Image extends BaseImage implements IImage { public int getDegreesRotated() { return mRotation; } - - protected void setDegreesRotated(int degrees) { - if (mRotation == degrees) return; - mRotation = degrees; - ContentValues values = new ContentValues(); - values.put(ImageColumns.ORIENTATION, mRotation); - mContentResolver.update(mUri, values, null, null); - - //TODO: Consider invalidate the cursor in container - // ((BaseImageList) getContainer()).invalidateCursor(); - } - - public boolean isReadonly() { - String mimeType = getMimeType(); - return !"image/jpeg".equals(mimeType) && !"image/png".equals(mimeType); - } - - public boolean isDrm() { - return false; - } - - /** - * Replaces the tag if already there. Otherwise, adds to the exif tags. - * @param tag - * @param value - */ - public void replaceExifTag(String tag, String value) { - if (mExif == null) { - loadExifData(); - } - mExif.setAttribute(tag, value); - } - - private void loadExifData() { - try { - mExif = new ExifInterface(mDataPath); - } catch (IOException ex) { - Log.e(TAG, "cannot read exif", ex); - } - } - - private void saveExifData() throws IOException { - if (mExif != null) { - mExif.saveAttributes(); - } - } - - private void setExifRotation(int degrees) { - try { - degrees %= 360; - if (degrees < 0) degrees += 360; - - int orientation = ExifInterface.ORIENTATION_NORMAL; - switch (degrees) { - case 0: - orientation = ExifInterface.ORIENTATION_NORMAL; - break; - case 90: - orientation = ExifInterface.ORIENTATION_ROTATE_90; - break; - case 180: - orientation = ExifInterface.ORIENTATION_ROTATE_180; - break; - case 270: - orientation = ExifInterface.ORIENTATION_ROTATE_270; - break; - } - - replaceExifTag(ExifInterface.TAG_ORIENTATION, - Integer.toString(orientation)); - saveExifData(); - } catch (Exception ex) { - Log.e(TAG, "unable to save exif data with new orientation " - + fullSizeImageUri(), ex); - } - } - - /** - * Save the rotated image by updating the Exif "Orientation" tag. - * @param degrees - */ - public boolean rotateImageBy(int degrees) { - int newDegrees = (getDegreesRotated() + degrees) % 360; - setExifRotation(newDegrees); - setDegreesRotated(newDegrees); - - return true; - } - - private static final String[] THUMB_PROJECTION = new String[] { - BaseColumns._ID, - }; - - public Bitmap thumbBitmap(boolean rotateAsNeeded) { - Bitmap bitmap = null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - bitmap = BitmapManager.instance().getThumbnail(mContentResolver, mId, - Images.Thumbnails.MINI_KIND, options, false); - - if (bitmap != null && rotateAsNeeded) { - bitmap = Util.rotate(bitmap, getDegreesRotated()); - } - - return bitmap; - } } diff --git a/src/com/android/camera/gallery/ImageList.java b/src/com/android/camera/gallery/ImageList.java index 20f9946..ae89a99 100644 --- a/src/com/android/camera/gallery/ImageList.java +++ b/src/com/android/camera/gallery/ImageList.java @@ -72,45 +72,28 @@ public class ImageList extends BaseImageList implements IImageList { static final String[] IMAGE_PROJECTION = new String[] { Media._ID, - Media.DATA, Media.DATE_TAKEN, Media.MINI_THUMB_MAGIC, Media.ORIENTATION, - Media.TITLE, - Media.MIME_TYPE, Media.DATE_MODIFIED}; private static final int INDEX_ID = 0; - private static final int INDEX_DATA_PATH = 1; - private static final int INDEX_DATE_TAKEN = 2; - private static final int INDEX_MINI_THUMB_MAGIC = 3; - private static final int INDEX_ORIENTATION = 4; - private static final int INDEX_TITLE = 5; - private static final int INDEX_MIME_TYPE = 6; - private static final int INDEX_DATE_MODIFIED = 7; - - @Override - protected long getImageId(Cursor cursor) { - return cursor.getLong(INDEX_ID); - } + private static final int INDEX_DATE_TAKEN = 1; + private static final int INDEX_MINI_THUMB_MAGIC = 2; + private static final int INDEX_ORIENTATION = 3; + private static final int INDEX_DATE_MODIFIED = 4; @Override protected BaseImage loadImageFromCursor(Cursor cursor) { long id = cursor.getLong(INDEX_ID); - String dataPath = cursor.getString(INDEX_DATA_PATH); long dateTaken = cursor.getLong(INDEX_DATE_TAKEN); if (dateTaken == 0) { dateTaken = cursor.getLong(INDEX_DATE_MODIFIED) * 1000; } long miniThumbMagic = cursor.getLong(INDEX_MINI_THUMB_MAGIC); int orientation = cursor.getInt(INDEX_ORIENTATION); - String title = cursor.getString(INDEX_TITLE); - String mimeType = cursor.getString(INDEX_MIME_TYPE); - if (title == null || title.length() == 0) { - title = dataPath; - } - return new Image(this, mContentResolver, id, cursor.getPosition(), - contentUri(id), dataPath, miniThumbMagic, mimeType, dateTaken, - title, orientation); + return new Image(mContentResolver, id, + contentUri(id), miniThumbMagic, dateTaken, + orientation); } } diff --git a/src/com/android/camera/gallery/ImageListUber.java b/src/com/android/camera/gallery/ImageListUber.java index 0756947..7a23aec 100644 --- a/src/com/android/camera/gallery/ImageListUber.java +++ b/src/com/android/camera/gallery/ImageListUber.java @@ -141,86 +141,6 @@ public class ImageListUber implements IImageList { return slot; } - public IImage getImageForUri(Uri uri) { - for (IImageList sublist : mSubList) { - IImage image = sublist.getImageForUri(uri); - if (image != null) return image; - } - return null; - } - - /** - * Modify the skip list when an image is deleted by finding - * the relevant entry in mSkipList and decrementing the - * counter. This is simple because deletion can never - * cause change the order of images. - */ - private void modifySkipCountForDeletedImage(int index) { - int skipCount = 0; - for (int i = 0, n = mSkipListSize; i < n; i++) { - long v = mSkipList[i]; - int offset = (int) (v & 0xFFFFFFFF); - if (skipCount + offset > index) { - mSkipList[i] = v - 1; - break; - } - skipCount += offset; - } - } - - private boolean removeImage(IImage image, int index) { - IImageList list = image.getContainer(); - if (list != null && list.removeImage(image)) { - modifySkipCountForDeletedImage(index); - return true; - } - return false; - } - - public boolean removeImage(IImage image) { - return removeImage(image, getImageIndex(image)); - } - - public boolean removeImageAt(int index) { - IImage image = getImageAt(index); - if (image == null) return false; - return removeImage(image, index); - } - - public synchronized int getImageIndex(IImage image) { - IImageList list = image.getContainer(); - int listIndex = Util.indexOf(mSubList, list); - if (listIndex == -1) { - throw new IllegalArgumentException(); - } - int listOffset = list.getImageIndex(image); - - // Similar algorithm as getImageAt(int index) - int skipCount = 0; - for (int i = 0, n = mSkipListSize; i < n; ++i) { - long value = mSkipList[i]; - int offset = (int) (value & 0xFFFFFFFF); - int which = (int) (value >> 32); - if (which == listIndex) { - if (listOffset < offset) { - return skipCount + listOffset; - } - listOffset -= offset; - } - skipCount += offset; - } - - for (; true; ++skipCount) { - MergeSlot slot = nextMergeSlot(); - if (slot == null) return -1; - if (slot.mImage == image) { - if (slot.next()) mQueue.add(slot); - return skipCount; - } - if (slot.next()) mQueue.add(slot); - } - } - private static class DescendingComparator implements Comparator { public int compare(MergeSlot m1, MergeSlot m2) { diff --git a/src/com/android/camera/gallery/SingleImageList.java b/src/com/android/camera/gallery/SingleImageList.java deleted file mode 100644 index c2ef371..0000000 --- a/src/com/android/camera/gallery/SingleImageList.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera.gallery; - -import android.content.ContentResolver; -import android.net.Uri; - -/** - * An implementation of interface IImageList which contains only - * one image. - */ -public class SingleImageList implements IImageList { - - @SuppressWarnings("unused") - private static final String TAG = "BaseImageList"; - - private IImage mSingleImage; - private Uri mUri; - - public SingleImageList(ContentResolver resolver, Uri uri) { - mUri = uri; - mSingleImage = new UriImage(this, resolver, uri); - } - - public int getCount() { - return 1; - } - - public int getImageIndex(IImage image) { - return image == mSingleImage ? 0 : -1; - } - - public IImage getImageAt(int i) { - return i == 0 ? mSingleImage : null; - } - - public boolean removeImage(IImage image) { - return false; - } - - public boolean removeImageAt(int index) { - return false; - } - - public IImage getImageForUri(Uri uri) { - return uri.equals(mUri) ? mSingleImage : null; - } - - public void close() { - mSingleImage = null; - mUri = null; - } -} diff --git a/src/com/android/camera/gallery/UriImage.java b/src/com/android/camera/gallery/UriImage.java deleted file mode 100644 index a229f76..0000000 --- a/src/com/android/camera/gallery/UriImage.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera.gallery; - -import com.android.camera.BitmapManager; -import com.android.camera.Util; - -import android.content.ContentResolver; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.InputStream; - -class UriImage implements IImage { - private static final String TAG = "UriImage"; - private final Uri mUri; - private final IImageList mContainer; - private final ContentResolver mContentResolver; - - UriImage(IImageList container, ContentResolver cr, Uri uri) { - mContainer = container; - mContentResolver = cr; - mUri = uri; - } - - public int getDegreesRotated() { - return 0; - } - - public String getDataPath() { - return mUri.getPath(); - } - - private InputStream getInputStream() { - try { - if (mUri.getScheme().equals("file")) { - return new java.io.FileInputStream(mUri.getPath()); - } else { - return mContentResolver.openInputStream(mUri); - } - } catch (FileNotFoundException ex) { - return null; - } - } - - private ParcelFileDescriptor getPFD() { - try { - if (mUri.getScheme().equals("file")) { - String path = mUri.getPath(); - return ParcelFileDescriptor.open(new File(path), - ParcelFileDescriptor.MODE_READ_ONLY); - } else { - return mContentResolver.openFileDescriptor(mUri, "r"); - } - } catch (FileNotFoundException ex) { - return null; - } - } - - public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels) { - return fullSizeBitmap(minSideLength, maxNumberOfPixels, - IImage.ROTATE_AS_NEEDED); - } - - public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels, - boolean rotateAsNeeded) { - try { - ParcelFileDescriptor pfdInput = getPFD(); - Bitmap b = Util.makeBitmap(minSideLength, maxNumberOfPixels, - pfdInput); - return b; - } catch (Exception ex) { - Log.e(TAG, "got exception decoding bitmap ", ex); - return null; - } - } - - public Uri fullSizeImageUri() { - return mUri; - } - - public InputStream fullSizeImageData() { - return getInputStream(); - } - - public Bitmap miniThumbBitmap() { - return thumbBitmap(IImage.ROTATE_AS_NEEDED); - } - - public String getTitle() { - return mUri.toString(); - } - - public Bitmap thumbBitmap(boolean rotateAsNeeded) { - return fullSizeBitmap(THUMBNAIL_TARGET_SIZE, THUMBNAIL_MAX_NUM_PIXELS, - rotateAsNeeded); - } - - private BitmapFactory.Options snifBitmapOptions() { - ParcelFileDescriptor input = getPFD(); - if (input == null) return null; - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapManager.instance().decodeFileDescriptor( - input.getFileDescriptor(), options); - return options; - } finally { - Util.closeSilently(input); - } - } - - public String getMimeType() { - BitmapFactory.Options options = snifBitmapOptions(); - return (options != null && options.outMimeType != null) - ? options.outMimeType - : ""; - } - - public int getHeight() { - BitmapFactory.Options options = snifBitmapOptions(); - return (options != null) ? options.outHeight : 0; - } - - public int getWidth() { - BitmapFactory.Options options = snifBitmapOptions(); - return (options != null) ? options.outWidth : 0; - } - - public IImageList getContainer() { - return mContainer; - } - - public long getDateTaken() { - return 0; - } - - public boolean isReadonly() { - return true; - } - - public boolean isDrm() { - return false; - } - - public boolean rotateImageBy(int degrees) { - return false; - } -} diff --git a/src/com/android/camera/gallery/VideoList.java b/src/com/android/camera/gallery/VideoList.java index 4c4bbd9..6331b9c 100644 --- a/src/com/android/camera/gallery/VideoList.java +++ b/src/com/android/camera/gallery/VideoList.java @@ -32,43 +32,26 @@ public class VideoList extends BaseImageList { private static final String[] VIDEO_PROJECTION = new String[] { Media._ID, - Media.DATA, Media.DATE_TAKEN, - Media.TITLE, Media.MINI_THUMB_MAGIC, - Media.MIME_TYPE, Media.DATE_MODIFIED}; private static final int INDEX_ID = 0; - private static final int INDEX_DATA_PATH = 1; - private static final int INDEX_DATE_TAKEN = 2; - private static final int INDEX_TITLE = 3; - private static final int INDEX_MIMI_THUMB_MAGIC = 4; - private static final int INDEX_MIME_TYPE = 5; - private static final int INDEX_DATE_MODIFIED = 6; - - @Override - protected long getImageId(Cursor cursor) { - return cursor.getLong(INDEX_ID); - } + private static final int INDEX_DATE_TAKEN = 1; + private static final int INDEX_MIMI_THUMB_MAGIC = 2; + private static final int INDEX_DATE_MODIFIED = 3; @Override protected BaseImage loadImageFromCursor(Cursor cursor) { long id = cursor.getLong(INDEX_ID); - String dataPath = cursor.getString(INDEX_DATA_PATH); long dateTaken = cursor.getLong(INDEX_DATE_TAKEN); if (dateTaken == 0) { dateTaken = cursor.getLong(INDEX_DATE_MODIFIED) * 1000; } long miniThumbMagic = cursor.getLong(INDEX_MIMI_THUMB_MAGIC); - String title = cursor.getString(INDEX_TITLE); - String mimeType = cursor.getString(INDEX_MIME_TYPE); - if (title == null || title.length() == 0) { - title = dataPath; - } - return new VideoObject(this, mContentResolver, - id, cursor.getPosition(), contentUri(id), dataPath, - miniThumbMagic, mimeType, dateTaken, title); + return new VideoObject(mContentResolver, + id, contentUri(id), + miniThumbMagic, dateTaken); } public VideoList(ContentResolver resolver, Uri uri, int sort, diff --git a/src/com/android/camera/gallery/VideoObject.java b/src/com/android/camera/gallery/VideoObject.java index 0a72209..9787f42 100644 --- a/src/com/android/camera/gallery/VideoObject.java +++ b/src/com/android/camera/gallery/VideoObject.java @@ -41,11 +41,11 @@ public class VideoObject extends BaseImage implements IImage { * @param id the image id of the image * @param cr the content resolver */ - protected VideoObject(BaseImageList container, ContentResolver cr, - long id, int index, Uri uri, String dataPath, long miniThumbMagic, - String mimeType, long dateTaken, String title) { - super(container, cr, id, index, uri, dataPath, miniThumbMagic, - mimeType, dateTaken, title); + protected VideoObject(ContentResolver cr, + long id, Uri uri, long miniThumbMagic, + long dateTaken) { + super(cr, id, uri, miniThumbMagic, + dateTaken); } @Override @@ -61,49 +61,6 @@ public class VideoObject extends BaseImage implements IImage { } @Override - public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels, - boolean rotateAsNeeded) { - return ThumbnailUtils.createVideoThumbnail(mDataPath); - } - - @Override - public InputStream fullSizeImageData() { - try { - InputStream input = mContentResolver.openInputStream( - fullSizeImageUri()); - return input; - } catch (IOException ex) { - return null; - } - } - - @Override - public int getHeight() { - return 0; - } - - @Override - public int getWidth() { - return 0; - } - - public boolean isReadonly() { - return false; - } - - public boolean isDrm() { - return false; - } - - public boolean rotateImageBy(int degrees) { - return false; - } - - public Bitmap thumbBitmap(boolean rotateAsNeeded) { - return fullSizeBitmap(THUMBNAIL_TARGET_SIZE, THUMBNAIL_MAX_NUM_PIXELS); - } - - @Override public Bitmap miniThumbBitmap() { try { long id = mId; diff --git a/tests/src/com/android/camera/BitmapManagerUnitTests.java b/tests/src/com/android/camera/BitmapManagerUnitTests.java deleted file mode 100644 index 7878b74..0000000 --- a/tests/src/com/android/camera/BitmapManagerUnitTests.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import com.android.camera.gallery.IImage; -import com.android.camera.gallery.IImageList; - -import android.content.Context; -import android.graphics.Bitmap; -import android.test.AndroidTestCase; - -/** - * BitmapManager's unit tests. - */ -public class BitmapManagerUnitTests extends AndroidTestCase { - IImageList mImageList; - IImage mImage; - BitmapManager mBitmapManager; - Context mContext; - - private class DecodeThread extends Thread { - Bitmap bitmap; - - public DecodeThread() { - } - - @Override - public void run() { - bitmap = mImage.thumbBitmap(IImage.ROTATE_AS_NEEDED); - } - - public Bitmap getBitmap() { - return bitmap; - } - } - - @Override - public void setUp() { - mContext = getContext(); - mBitmapManager = BitmapManager.instance(); - mImageList = ImageManager.makeImageList( - mContext.getContentResolver(), - ImageManager.DataLocation.ALL, - ImageManager.INCLUDE_IMAGES, - ImageManager.SORT_DESCENDING, - null); - mImage = mImageList.getImageAt(0); - } - - public void testSingleton() { - BitmapManager manager = BitmapManager.instance(); - assertNotNull(manager); - assertNotNull(mBitmapManager); - assertSame(manager, mBitmapManager); - } - - public void testCanThreadDecoding() { - Thread t = new DecodeThread(); - - // By default all threads can decode. - assertTrue(mBitmapManager.canThreadDecoding(t)); - - // Disallow thread t to decode. - mBitmapManager.cancelThreadDecoding(t, mContext.getContentResolver()); - assertFalse(mBitmapManager.canThreadDecoding(t)); - - // Allow thread t to decode again. - mBitmapManager.allowThreadDecoding(t); - assertTrue(mBitmapManager.canThreadDecoding(t)); - } - - public void testDefaultAllowDecoding() { - DecodeThread t = new DecodeThread(); - try { - t.start(); - t.join(); - } catch (InterruptedException ex) { - } finally { - assertNotNull(t.getBitmap()); - } - } - - public void testCancelDecoding() { - DecodeThread t = new DecodeThread(); - mBitmapManager.cancelThreadDecoding(t, mContext.getContentResolver()); - try { - t.start(); - t.join(); - } catch (InterruptedException ex) { - } finally { - assertNull(t.getBitmap()); - } - } - - public void testAllowDecoding() { - DecodeThread t = new DecodeThread(); - mBitmapManager.cancelThreadDecoding(t, mContext.getContentResolver()); - mBitmapManager.allowThreadDecoding(t); - try { - t.start(); - t.join(); - } catch (InterruptedException ex) { - } finally { - assertNotNull(t.getBitmap()); - } - } - - public void testThreadDecoding() { - DecodeThread t1 = new DecodeThread(); - DecodeThread t2 = new DecodeThread(); - mBitmapManager.allowThreadDecoding(t1); - mBitmapManager.cancelThreadDecoding(t2, mContext.getContentResolver()); - t1.start(); - t2.start(); - - try { - t1.join(); - t2.join(); - } catch (InterruptedException ex) { - } finally { - assertTrue(mBitmapManager.canThreadDecoding(t1)); - assertNotNull(t1.getBitmap()); - assertFalse(mBitmapManager.canThreadDecoding(t2)); - assertNull(t2.getBitmap()); - } - } - - @Override - public String toString() { - return "BitmapManagerUnitTest"; - } -} -- 2.11.0