diff options
author | ztenghui <ztenghui@google.com> | 2013-08-28 09:30:18 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-08-28 09:30:18 -0700 |
commit | 7cd724ae6acf09a641110e0bb46d28f9a932c1e8 (patch) | |
tree | 1f0a8a2dab964cb9f08d6e1608abf80eb6fecbc4 | |
parent | f4d1ee6ea91d630178595d955b6b96cdb2a1fe4e (diff) | |
parent | 80e2e1eca93d1df72420eb6d1b7db88460620b6e (diff) | |
download | android_packages_apps_Snap-7cd724ae6acf09a641110e0bb46d28f9a932c1e8.tar.gz android_packages_apps_Snap-7cd724ae6acf09a641110e0bb46d28f9a932c1e8.tar.bz2 android_packages_apps_Snap-7cd724ae6acf09a641110e0bb46d28f9a932c1e8.zip |
am 80e2e1ec: Merge "Add rotate support in Film Strip" into gb-ub-photos-carlsbad
* commit '80e2e1eca93d1df72420eb6d1b7db88460620b6e':
Add rotate support in Film Strip
-rw-r--r-- | src/com/android/camera/CameraActivity.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/Exif.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/MediaSaveService.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/PhotoModule.java | 6 | ||||
-rw-r--r-- | src/com/android/camera/Storage.java | 5 | ||||
-rw-r--r-- | src/com/android/camera/VideoModule.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/data/CameraDataAdapter.java | 5 | ||||
-rw-r--r-- | src/com/android/camera/data/LocalData.java | 30 | ||||
-rw-r--r-- | src/com/android/camera/data/LocalMediaData.java | 332 | ||||
-rw-r--r-- | src/com/android/camera/data/MediaDetails.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/data/RotationTask.java | 161 | ||||
-rw-r--r-- | src/com/android/camera/data/SimpleViewData.java | 35 | ||||
-rw-r--r-- | src/com/android/camera/exif/ByteBufferInputStream.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/CountedDataInputStream.java (renamed from gallerycommon/src/com/android/gallery3d/exif/CountedDataInputStream.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifData.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifData.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifInterface.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifInterface.java) | 6 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifInvalidFormatException.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifInvalidFormatException.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifModifier.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifOutputStream.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifParser.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifParser.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifReader.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifReader.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/ExifTag.java (renamed from gallerycommon/src/com/android/gallery3d/exif/ExifTag.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/IfdData.java (renamed from gallerycommon/src/com/android/gallery3d/exif/IfdData.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/IfdId.java (renamed from gallerycommon/src/com/android/gallery3d/exif/IfdId.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/JpegHeader.java (renamed from gallerycommon/src/com/android/gallery3d/exif/JpegHeader.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/OrderedDataOutputStream.java (renamed from gallerycommon/src/com/android/gallery3d/exif/OrderedDataOutputStream.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/exif/Rational.java (renamed from gallerycommon/src/com/android/gallery3d/exif/Rational.java) | 2 | ||||
-rw-r--r-- | src/com/android/camera/ui/FilmStripView.java | 4 |
28 files changed, 437 insertions, 189 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 0603dd36f..3a3dd0cc1 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -523,10 +523,10 @@ public class CameraActivity extends Activity return true; } case R.id.action_rotate_ccw: - // TODO: add the functionality. + localData.rotate90Degrees(this, mDataAdapter, currentDataId, false); return true; case R.id.action_rotate_cw: - // TODO: add the functionality. + localData.rotate90Degrees(this, mDataAdapter, currentDataId, true); return true; case R.id.action_crop: // TODO: add the functionality. diff --git a/src/com/android/camera/Exif.java b/src/com/android/camera/Exif.java index c6ec6af50..91aca4505 100644 --- a/src/com/android/camera/Exif.java +++ b/src/com/android/camera/Exif.java @@ -18,7 +18,7 @@ package com.android.camera; import android.util.Log; -import com.android.gallery3d.exif.ExifInterface; +import com.android.camera.exif.ExifInterface; import java.io.IOException; diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java index cec8b4329..988f17f94 100644 --- a/src/com/android/camera/MediaSaveService.java +++ b/src/com/android/camera/MediaSaveService.java @@ -29,7 +29,7 @@ import android.os.IBinder; import android.provider.MediaStore.Video; import android.util.Log; -import com.android.gallery3d.exif.ExifInterface; +import com.android.camera.exif.ExifInterface; import java.io.File; diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index bb486cb62..f56bd68f0 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -56,6 +56,9 @@ import com.android.camera.CameraManager.CameraAFMoveCallback; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; import com.android.camera.CameraManager.CameraShutterCallback; +import com.android.camera.exif.ExifInterface; +import com.android.camera.exif.ExifTag; +import com.android.camera.exif.Rational; import com.android.camera.ui.CountDownView.OnCountDownFinishedListener; import com.android.camera.ui.PopupManager; import com.android.camera.ui.RotateTextToast; @@ -63,9 +66,6 @@ import com.android.camera.util.ApiHelper; import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import com.android.camera2.R; -import com.android.gallery3d.exif.ExifInterface; -import com.android.gallery3d.exif.ExifTag; -import com.android.gallery3d.exif.Rational; import java.io.File; import java.io.FileNotFoundException; diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java index aa2a9721b..8aa3eca52 100644 --- a/src/com/android/camera/Storage.java +++ b/src/com/android/camera/Storage.java @@ -32,8 +32,9 @@ import android.provider.MediaStore.Images.ImageColumns; import android.provider.MediaStore.MediaColumns; import android.util.Log; +import com.android.camera.data.LocalData; +import com.android.camera.exif.ExifInterface; import com.android.camera.util.ApiHelper; -import com.android.gallery3d.exif.ExifInterface; public class Storage { private static final String TAG = "CameraStorage"; @@ -104,7 +105,7 @@ public class Storage { values.put(ImageColumns.TITLE, title); values.put(ImageColumns.DISPLAY_NAME, title + ".jpg"); values.put(ImageColumns.DATE_TAKEN, date); - values.put(ImageColumns.MIME_TYPE, "image/jpeg"); + values.put(ImageColumns.MIME_TYPE, LocalData.MIME_TYPE_JPEG); // Clockwise rotation in degrees. 0, 90, 180, or 270. values.put(ImageColumns.ORIENTATION, orientation); values.put(ImageColumns.DATA, path); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index bd2658e1b..53cb43ae0 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -56,6 +56,7 @@ import android.widget.Toast; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; import com.android.camera.app.OrientationManager; +import com.android.camera.exif.ExifInterface; import com.android.camera.util.ApiHelper; import com.android.camera.util.AccessibilityUtils; import com.android.camera.ui.PopupManager; @@ -63,7 +64,6 @@ import com.android.camera.ui.RotateTextToast; import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import com.android.camera2.R; -import com.android.gallery3d.exif.ExifInterface; import java.io.File; import java.io.IOException; diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java index 2d19a5ee6..af9fbb8f3 100644 --- a/src/com/android/camera/data/CameraDataAdapter.java +++ b/src/com/android/camera/data/CameraDataAdapter.java @@ -219,8 +219,9 @@ public class CameraDataAdapter implements LocalDataAdapter { } LocalData data = mImages.get(pos); - if (data.refresh(resolver)) { - updateData(pos, data); + LocalData refreshedData = data.refresh(resolver); + if (refreshedData != null) { + updateData(pos, refreshedData); } } diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java index 11d36da82..45bcb9ffa 100644 --- a/src/com/android/camera/data/LocalData.java +++ b/src/com/android/camera/data/LocalData.java @@ -29,10 +29,15 @@ import java.util.Comparator; /** * An abstract interface that represents the local media data. Also implements * Comparable interface so we can sort in DataAdapter. + * Note that all the sub-class of LocalData are designed to be immutable, i.e: + * all the members need to be final, and there is no setter. In this way, we + * can guarantee thread safety for LocalData. */ public interface LocalData extends FilmStripView.ImageData { static final String TAG = "CAM_LocalData"; + public static final String MIME_TYPE_JPEG = "image/jpeg"; + public static final int ACTION_NONE = 0; public static final int ACTION_PLAY = 1; public static final int ACTION_DELETE = (1 << 1); @@ -97,6 +102,17 @@ public interface LocalData extends FilmStripView.ImageData { /** Removes the data from the storage if possible. */ boolean delete(Context c); + /** + * Rotate the image in 90 degrees. This is a no-op for non-image. + * + * @param context Used to update the content provider when rotation is done. + * @param adapter Used to update the view. + * @param currentDataId Used to update the view. + * @param clockwise True if the rotation goes clockwise. + */ + void rotate90Degrees(Context context, LocalDataAdapter adapter, + int currentDataId, boolean clockwise); + void onFullScreen(boolean fullScreen); /** Returns {@code true} if it allows swipe to filmstrip in full screen. */ @@ -137,12 +153,17 @@ public interface LocalData extends FilmStripView.ImageData { int getLocalDataType(); /** + * @return The size of the data in bytes + */ + long getSizeInBytes(); + + /** * Refresh the data content. * * @param resolver {@link ContentResolver} to refresh the data. - * @return {@code true} if success, {@code false} otherwise. + * @return A new LocalData object if success, null otherwise. */ - boolean refresh(ContentResolver resolver); + LocalData refresh(ContentResolver resolver); static class NewestFirstComparator implements Comparator<LocalData> { @@ -170,5 +191,10 @@ public interface LocalData extends FilmStripView.ImageData { return cmp; } } + + /** + * @return the Id of the data. + */ + long getId(); } diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java index 88f127f3e..f692cbcc0 100644 --- a/src/com/android/camera/data/LocalMediaData.java +++ b/src/com/android/camera/data/LocalMediaData.java @@ -52,18 +52,18 @@ import java.util.Locale; * return a bitmap. */ public abstract class LocalMediaData implements LocalData { - protected long id; - protected String title; - protected String mimeType; - protected long dateTakenInSeconds; - protected long dateModifiedInSeconds; - protected String path; + protected final long mId; + protected final String mTitle; + protected final String mMimeType; + protected final long mDateTakenInSeconds; + protected final long mDateModifiedInSeconds; + protected final String mPath; // width and height should be adjusted according to orientation. - protected int width; - protected int height; - protected long sizeInBytes; - protected double latitude; - protected double longitude; + protected final int mWidth; + protected final int mHeight; + protected final long mSizeInBytes; + protected final double mLatitude; + protected final double mLongitude; /** The panorama metadata information of this media data. */ protected PhotoSphereHelper.PanoramaMetadata mPanoramaMetadata; @@ -77,34 +77,62 @@ public abstract class LocalMediaData implements LocalData { */ protected Boolean mUsing = false; + public LocalMediaData (long id, String title, String mimeType, + long dateTakenInSeconds, long dateModifiedInSeconds, String path, + int width, int height, long sizeInBytes, double latitude, + double longitude) { + mId = id; + mTitle = new String(title); + mMimeType = new String(mimeType); + mDateTakenInSeconds = dateTakenInSeconds; + mDateModifiedInSeconds = dateModifiedInSeconds; + mPath = new String(path); + mWidth = width; + mHeight = height; + mSizeInBytes = sizeInBytes; + mLatitude = latitude; + mLongitude = longitude; + return; + } + @Override public long getDateTaken() { - return dateTakenInSeconds; + return mDateTakenInSeconds; } @Override public long getDateModified() { - return dateModifiedInSeconds; + return mDateModifiedInSeconds; + } + + @Override + public long getId() { + return mId; } @Override public String getTitle() { - return new String(title); + return new String(mTitle); } @Override public int getWidth() { - return width; + return mWidth; } @Override public int getHeight() { - return height; + return mHeight; } @Override public String getPath() { - return path; + return mPath; + } + + @Override + public long getSizeInBytes() { + return mSizeInBytes; } @Override @@ -119,7 +147,7 @@ public abstract class LocalMediaData implements LocalData { @Override public boolean delete(Context ctx) { - File f = new File(path); + File f = new File(mPath); return f.delete(); } @@ -199,11 +227,11 @@ public abstract class LocalMediaData implements LocalData { @Override public double[] getLatLong() { - if (latitude == 0 && longitude == 0) { + if (mLatitude == 0 && mLongitude == 0) { return null; } return new double[] { - latitude, longitude + mLatitude, mLongitude }; } @@ -215,25 +243,25 @@ public abstract class LocalMediaData implements LocalData { @Override public String getMimeType() { - return mimeType; + return mMimeType; } @Override public MediaDetails getMediaDetails(Context context) { DateFormat dateFormatter = DateFormat.getDateTimeInstance(); MediaDetails mediaDetails = new MediaDetails(); - mediaDetails.addDetail(MediaDetails.INDEX_TITLE, title); - mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, width); - mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, height); - mediaDetails.addDetail(MediaDetails.INDEX_PATH, path); + mediaDetails.addDetail(MediaDetails.INDEX_TITLE, mTitle); + mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, mWidth); + mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, mHeight); + mediaDetails.addDetail(MediaDetails.INDEX_PATH, mPath); mediaDetails.addDetail(MediaDetails.INDEX_DATETIME, - dateFormatter.format(new Date(dateModifiedInSeconds * 1000))); - if (sizeInBytes > 0) { - mediaDetails.addDetail(MediaDetails.INDEX_SIZE, sizeInBytes); + dateFormatter.format(new Date(mDateModifiedInSeconds * 1000))); + if (mSizeInBytes > 0) { + mediaDetails.addDetail(MediaDetails.INDEX_SIZE, mSizeInBytes); } - if (latitude != 0 && longitude != 0) { - String locationString = String.format(Locale.getDefault(), "%f, %f", latitude, - longitude); + if (mLatitude != 0 && mLongitude != 0) { + String locationString = String.format(Locale.getDefault(), "%f, %f", mLatitude, + mLongitude); mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, locationString); } return mediaDetails; @@ -245,7 +273,7 @@ public abstract class LocalMediaData implements LocalData { protected abstract BitmapLoadTask getBitmapLoadTask( ImageView v, int decodeWidth, int decodeHeight); - public static class PhotoData extends LocalMediaData { + public static final class PhotoData extends LocalMediaData { private static final String TAG = "CAM_PhotoData"; public static final int COL_ID = 0; @@ -293,54 +321,69 @@ public abstract class LocalMediaData implements LocalData { private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024]; /** from MediaStore, can only be 0, 90, 180, 270 */ - public int orientation; + private final int mOrientation; + + public PhotoData(long id, String title, String mimeType, + long dateTakenInSeconds, long dateModifiedInSeconds, + String path, int orientation, int width, int height, + long sizeInBytes, double latitude, double longitude) { + super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds, + path, width, height, sizeInBytes, latitude, longitude); + mOrientation = orientation; + } static PhotoData buildFromCursor(Cursor c) { - PhotoData d = new PhotoData(); - d.id = c.getLong(COL_ID); - d.title = c.getString(COL_TITLE); - d.mimeType = c.getString(COL_MIME_TYPE); - d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); - d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); - d.path = c.getString(COL_DATA); - d.orientation = c.getInt(COL_ORIENTATION); - d.width = c.getInt(COL_WIDTH); - d.height = c.getInt(COL_HEIGHT); - if (d.width <= 0 || d.height <= 0) { + long id = c.getLong(COL_ID); + String title = c.getString(COL_TITLE); + String mimeType = c.getString(COL_MIME_TYPE); + long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); + long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); + String path = c.getString(COL_DATA); + int orientation = c.getInt(COL_ORIENTATION); + int width = c.getInt(COL_WIDTH); + int height = c.getInt(COL_HEIGHT); + if (width <= 0 || height <= 0) { Log.w(TAG, "Warning! zero dimension for " - + d.path + ":" + d.width + "x" + d.height); + + path + ":" + width + "x" + height); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; - BitmapFactory.decodeFile(d.path, opts); + BitmapFactory.decodeFile(path, opts); if (opts.outWidth != -1 && opts.outHeight != -1) { - d.width = opts.outWidth; - d.height = opts.outHeight; + width = opts.outWidth; + height = opts.outHeight; } else { - Log.w(TAG, "Warning! dimension decode failed for " + d.path); - Bitmap b = BitmapFactory.decodeFile(d.path); + Log.w(TAG, "Warning! dimension decode failed for " + path); + Bitmap b = BitmapFactory.decodeFile(path); if (b == null) { return null; } - d.width = b.getWidth(); - d.height = b.getHeight(); + width = b.getWidth(); + height = b.getHeight(); } } - if (d.orientation == 90 || d.orientation == 270) { - int b = d.width; - d.width = d.height; - d.height = b; + if (orientation == 90 || orientation == 270) { + int b = width; + width = height; + height = b; } - d.sizeInBytes = c.getLong(COL_SIZE); - d.latitude = c.getDouble(COL_LATITUDE); - d.longitude = c.getDouble(COL_LONGITUDE); - return d; + long sizeInBytes = c.getLong(COL_SIZE); + double latitude = c.getDouble(COL_LATITUDE); + double longitude = c.getDouble(COL_LONGITUDE); + PhotoData result = new PhotoData(id, title, mimeType, dateTakenInSeconds, + dateModifiedInSeconds, path, orientation, width, height, + sizeInBytes, latitude, longitude); + return result; + } + + public int getOrientation() { + return mOrientation; } @Override public String toString() { - return "Photo:" + ",data=" + path + ",mimeType=" + mimeType - + "," + width + "x" + height + ",orientation=" + orientation - + ",date=" + new Date(dateTakenInSeconds); + return "Photo:" + ",data=" + mPath + ",mimeType=" + mMimeType + + "," + mWidth + "x" + mHeight + ",orientation=" + mOrientation + + ",date=" + new Date(mDateTakenInSeconds); } @Override @@ -361,20 +404,20 @@ public abstract class LocalMediaData implements LocalData { @Override public boolean delete(Context c) { ContentResolver cr = c.getContentResolver(); - cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + id, null); + cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + mId, null); return super.delete(c); } @Override public Uri getContentUri() { Uri baseUri = CONTENT_URI; - return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); + return baseUri.buildUpon().appendPath(String.valueOf(mId)).build(); } @Override public MediaDetails getMediaDetails(Context context) { MediaDetails mediaDetails = super.getMediaDetails(context); - MediaDetails.extractExifInfo(mediaDetails, path); + MediaDetails.extractExifInfo(mediaDetails, mPath); return mediaDetails; } @@ -391,23 +434,14 @@ public abstract class LocalMediaData implements LocalData { } @Override - public boolean refresh(ContentResolver resolver) { + public LocalData refresh(ContentResolver resolver) { Cursor c = resolver.query( getContentUri(), QUERY_PROJECTION, null, null, null); if (c == null || !c.moveToFirst()) { - return false; + return null; } PhotoData newData = buildFromCursor(c); - id = newData.id; - title = newData.title; - mimeType = newData.mimeType; - dateTakenInSeconds = newData.dateTakenInSeconds; - dateModifiedInSeconds = newData.dateModifiedInSeconds; - path = newData.path; - orientation = newData.orientation; - width = newData.width; - height = newData.height; - return true; + return newData; } @Override @@ -434,9 +468,9 @@ public abstract class LocalMediaData implements LocalData { @Override protected Bitmap doInBackground(Void... v) { int sampleSize = 1; - if (width > mDecodeWidth || height > mDecodeHeight) { - int heightRatio = Math.round((float) height / (float) mDecodeHeight); - int widthRatio = Math.round((float) width / (float) mDecodeWidth); + if (mWidth > mDecodeWidth || mHeight > mDecodeHeight) { + int heightRatio = Math.round((float) mHeight / (float) mDecodeHeight); + int widthRatio = Math.round((float) mWidth / (float) mDecodeWidth); sampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } @@ -446,21 +480,30 @@ public abstract class LocalMediaData implements LocalData { if (isCancelled() || !isUsing()) { return null; } - Bitmap b = BitmapFactory.decodeFile(path, opts); - if (orientation != 0) { + Bitmap b = BitmapFactory.decodeFile(mPath, opts); + if (mOrientation != 0) { if (isCancelled() || !isUsing()) { return null; } Matrix m = new Matrix(); - m.setRotate(orientation); + m.setRotate(mOrientation); b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false); } return b; } } + + @Override + public void rotate90Degrees(Context context, LocalDataAdapter adapter, + int currentDataId, boolean clockwise) { + RotationTask task = new RotationTask(context, adapter, + currentDataId, clockwise); + task.execute(this); + return; + } } - public static class VideoData extends LocalMediaData { + public static final class VideoData extends LocalMediaData { public static final int COL_ID = 0; public static final int COL_TITLE = 1; public static final int COL_MIME_TYPE = 2; @@ -505,26 +548,31 @@ public abstract class LocalMediaData implements LocalData { MediaStore.Video.VideoColumns.DURATION // 12 long }; - private Uri mPlayUri; - /** The duration in milliseconds. */ - private long durationInSeconds; + private long mDurationInSeconds; + + public VideoData(long id, String title, String mimeType, + long dateTakenInSeconds, long dateModifiedInSeconds, + String path, int width, int height, long sizeInBytes, + double latitude, double longitude, long durationInSeconds) { + super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds, + path, width, height, sizeInBytes, latitude, longitude); + mDurationInSeconds = durationInSeconds; + } static VideoData buildFromCursor(Cursor c) { - VideoData d = new VideoData(); - d.id = c.getLong(COL_ID); - d.title = c.getString(COL_TITLE); - d.mimeType = c.getString(COL_MIME_TYPE); - d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); - d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); - d.path = c.getString(COL_DATA); - d.width = c.getInt(COL_WIDTH); - d.height = c.getInt(COL_HEIGHT); - d.mPlayUri = d.getContentUri(); + long id = c.getLong(COL_ID); + String title = c.getString(COL_TITLE); + String mimeType = c.getString(COL_MIME_TYPE); + long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); + long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); + String path = c.getString(COL_DATA); + int width = c.getInt(COL_WIDTH); + int height = c.getInt(COL_HEIGHT); MediaMetadataRetriever retriever = new MediaMetadataRetriever(); String rotation = null; try { - retriever.setDataSource(d.path); + retriever.setDataSource(path); } catch (IllegalArgumentException ex) { retriever.release(); Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:" @@ -533,33 +581,43 @@ public abstract class LocalMediaData implements LocalData { } rotation = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); - if (d.width == 0 || d.height == 0) { - retrieveVideoDimension(retriever, d); + + // Extracts video height/width if available. If unavailable, set to 0. + if (width == 0 || height == 0) { + String val = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); + width = (val == null) ? 0 : Integer.parseInt(val); + val = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + height = (val == null) ? 0 : Integer.parseInt(val); } retriever.release(); - if (d.width == 0 || d.height == 0) { + if (width == 0 || height == 0) { // Width or height is still not available. - Log.e(TAG, "Unable to retrieve dimension of video:" + d.path); + Log.e(TAG, "Unable to retrieve dimension of video:" + path); return null; } if (rotation != null && (rotation.equals("90") || rotation.equals("270"))) { - int b = d.width; - d.width = d.height; - d.height = b; + int b = width; + width = height; + height = b; } - d.sizeInBytes = c.getLong(COL_SIZE); - d.latitude = c.getDouble(COL_LATITUDE); - d.longitude = c.getDouble(COL_LONGITUDE); - d.durationInSeconds = c.getLong(COL_DURATION) / 1000; + long sizeInBytes = c.getLong(COL_SIZE); + double latitude = c.getDouble(COL_LATITUDE); + double longitude = c.getDouble(COL_LONGITUDE); + long durationInSeconds = c.getLong(COL_DURATION) / 1000; + VideoData d = new VideoData(id, title, mimeType, dateTakenInSeconds, + dateModifiedInSeconds, path, width, height, sizeInBytes, + latitude, longitude, durationInSeconds); return d; } @Override public String toString() { - return "Video:" + ",data=" + path + ",mimeType=" + mimeType - + "," + width + "x" + height + ",date=" + new Date(dateTakenInSeconds); + return "Video:" + ",data=" + mPath + ",mimeType=" + mMimeType + + "," + mWidth + "x" + mHeight + ",date=" + new Date(mDateTakenInSeconds); } @Override @@ -580,20 +638,20 @@ public abstract class LocalMediaData implements LocalData { @Override public boolean delete(Context ctx) { ContentResolver cr = ctx.getContentResolver(); - cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + id, null); + cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + mId, null); return super.delete(ctx); } @Override public Uri getContentUri() { Uri baseUri = CONTENT_URI; - return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); + return baseUri.buildUpon().appendPath(String.valueOf(mId)).build(); } @Override public MediaDetails getMediaDetails(Context context) { MediaDetails mediaDetails = super.getMediaDetails(context); - String duration = MediaDetails.formatDuration(context, durationInSeconds); + String duration = MediaDetails.formatDuration(context, mDurationInSeconds); mediaDetails.addDetail(MediaDetails.INDEX_DURATION, duration); return mediaDetails; } @@ -604,26 +662,14 @@ public abstract class LocalMediaData implements LocalData { } @Override - public boolean refresh(ContentResolver resolver) { + public LocalData refresh(ContentResolver resolver) { Cursor c = resolver.query( getContentUri(), QUERY_PROJECTION, null, null, null); if (c == null || !c.moveToFirst()) { - return false; + return null; } VideoData newData = buildFromCursor(c); - if (newData == null) { - return false; - } - id = newData.id; - title = newData.title; - mimeType = newData.mimeType; - dateTakenInSeconds = newData.dateTakenInSeconds; - dateModifiedInSeconds = newData.dateModifiedInSeconds; - path = newData.path; - width = newData.width; - height = newData.height; - mPlayUri = newData.mPlayUri; - return true; + return newData; } @Override @@ -647,7 +693,7 @@ public abstract class LocalMediaData implements LocalData { icon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - CameraUtil.playVideo(ctx, mPlayUri, title); + CameraUtil.playVideo(ctx, getContentUri(), mTitle); } }); @@ -680,7 +726,7 @@ public abstract class LocalMediaData implements LocalData { return null; } MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - retriever.setDataSource(path); + retriever.setDataSource(mPath); byte[] data = retriever.getEmbeddedPicture(); Bitmap bitmap = null; if (isCancelled() || !isUsing()) { @@ -698,20 +744,12 @@ public abstract class LocalMediaData implements LocalData { } } - /** - * Extracts video height/width if available. If unavailable, set to 0. - * - * @param retriever An initialized metadata retriever. - * @param d The {@link VideoData} whose width/height are to update. - */ - private static void retrieveVideoDimension( - MediaMetadataRetriever retriever, VideoData d) { - String val = retriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); - d.width = (val == null) ? 0 : Integer.parseInt(val); - val = retriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); - d.height = (val == null) ? 0 : Integer.parseInt(val); + @Override + public void rotate90Degrees(Context context, LocalDataAdapter adapter, + int currentDataId, boolean clockwise) { + // We don't support rotation for video data. + Log.e(TAG, "Unexpected call in rotate90Degrees()"); + return; } } @@ -733,7 +771,7 @@ public abstract class LocalMediaData implements LocalData { return; } if (bitmap == null) { - Log.e(TAG, "Failed decoding bitmap for file:" + path); + Log.e(TAG, "Failed decoding bitmap for file:" + mPath); return; } BitmapDrawable d = new BitmapDrawable(bitmap); diff --git a/src/com/android/camera/data/MediaDetails.java b/src/com/android/camera/data/MediaDetails.java index a614b8d84..dd4c147cc 100644 --- a/src/com/android/camera/data/MediaDetails.java +++ b/src/com/android/camera/data/MediaDetails.java @@ -20,9 +20,9 @@ import android.content.Context; import android.util.Log; import android.util.SparseIntArray; +import com.android.camera.exif.ExifInterface; +import com.android.camera.exif.ExifTag; import com.android.camera2.R; -import com.android.gallery3d.exif.ExifInterface; -import com.android.gallery3d.exif.ExifTag; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/src/com/android/camera/data/RotationTask.java b/src/com/android/camera/data/RotationTask.java new file mode 100644 index 000000000..8daead156 --- /dev/null +++ b/src/com/android/camera/data/RotationTask.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 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.data; + +import android.app.ProgressDialog; +import android.content.ContentValues; +import android.content.Context; +import android.os.AsyncTask; +import android.provider.MediaStore.Images; +import android.util.Log; + +import com.android.camera.data.LocalMediaData.PhotoData; +import com.android.camera.exif.ExifInterface; +import com.android.camera.exif.ExifTag; +import com.android.camera2.R; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * RotationTask can be used to rotate a {@link LocalData} by updating the exif + * data from jpeg file. Note that only {@link PhotoData} can be rotated. + */ +public class RotationTask extends AsyncTask<LocalData, Void, LocalData> { + private static final String TAG = "CAM_RotationTask"; + private final Context mContext; + private final LocalDataAdapter mAdapter; + private final int mCurrentDataId; + private final boolean mClockwise; + private ProgressDialog mProgress; + + public RotationTask(Context context, LocalDataAdapter adapter, + int currentDataId, boolean clockwise) { + mContext = context; + mAdapter = adapter; + mCurrentDataId = currentDataId; + mClockwise = clockwise; + } + + @Override + protected void onPreExecute() { + // Show a progress bar since the rotation could take long. + mProgress = new ProgressDialog(mContext); + int titleStringId = mClockwise ? R.string.rotate_right : R.string.rotate_left; + mProgress.setTitle(mContext.getString(titleStringId)); + mProgress.setMessage(mContext.getString(R.string.please_wait)); + mProgress.setCancelable(false); + mProgress.show(); + } + + @Override + protected LocalData doInBackground(LocalData... data) { + return rotateInJpegExif(data[0]); + } + + /** + * Rotates the image by updating the exif. Done in background thread. + * The worst case is the whole file needed to be re-written with + * modified exif data. + * + * @return A new {@link LocalData} object which containing the new info. + */ + private LocalData rotateInJpegExif(LocalData data) { + if (!(data instanceof PhotoData)) { + Log.w(TAG, "Rotation can only happen on PhotoData."); + return null; + } + + PhotoData imageData = (PhotoData) data; + int originRotation = imageData.getOrientation(); + int finalRotationDegrees; + if (mClockwise) { + finalRotationDegrees = (originRotation + 90) % 360; + } else { + finalRotationDegrees = (originRotation + 270) % 360; + } + + String filePath = imageData.getPath(); + ContentValues values = new ContentValues(); + boolean success = false; + int newOrientation = 0; + if (imageData.getMimeType().equalsIgnoreCase(LocalData.MIME_TYPE_JPEG)) { + ExifInterface exifInterface = new ExifInterface(); + ExifTag tag = exifInterface.buildTag( + ExifInterface.TAG_ORIENTATION, + ExifInterface.getOrientationValueForRotation( + finalRotationDegrees)); + if (tag != null) { + exifInterface.setTag(tag); + try { + exifInterface.forceRewriteExif(filePath); + long fileSize = new File(filePath).length(); + values.put(Images.Media.SIZE, fileSize); + newOrientation = finalRotationDegrees; + success = true; + } catch (FileNotFoundException e) { + Log.w(TAG, "Cannot find file to set exif: " + filePath); + } catch (IOException e) { + Log.w(TAG, "Cannot set exif data: " + filePath); + } + } else { + Log.w(TAG, "Cannot build tag: " + ExifInterface.TAG_ORIENTATION); + } + } + + PhotoData result = null; + if (success) { + // Swap width and height after rotation. + int oldWidth = imageData.getWidth(); + int newWidth = imageData.getHeight(); + int newHeight = oldWidth; + values.put(Images.Media.WIDTH, newWidth); + values.put(Images.Media.HEIGHT, newHeight); + + // MediaStore using SQLite is thread safe. + values.put(Images.Media.ORIENTATION, finalRotationDegrees); + mContext.getContentResolver().update(imageData.getContentUri(), + values, "_id=?", + new String[] { + String.valueOf(imageData.getId()) }); + + double[] latLong = data.getLatLong(); + double latitude = 0; + double longitude = 0; + if (latLong != null) { + latitude = latLong[0]; + longitude = latLong[1]; + } + + result = new PhotoData(data.getId(), data.getTitle(), + data.getMimeType(), data.getDateTaken(), data.getDateModified(), + data.getPath(), newOrientation, newWidth, newHeight, + data.getSizeInBytes(), latitude, longitude); + } + + return result; + } + + @Override + protected void onPostExecute(LocalData result) { + mProgress.dismiss(); + if (result != null) { + mAdapter.updateData(mCurrentDataId, result); + } + } +} diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java index 06ff3501b..a49d3eac8 100644 --- a/src/com/android/camera/data/SimpleViewData.java +++ b/src/com/android/camera/data/SimpleViewData.java @@ -20,6 +20,7 @@ import android.content.ContentResolver; import android.content.Context; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.util.Log; import android.view.View; import com.android.camera.ui.FilmStripView; @@ -29,11 +30,13 @@ import com.android.camera.util.PhotoSphereHelper; * A LocalData that does nothing but only shows a view. */ public class SimpleViewData implements LocalData { - private int mWidth; - private int mHeight; - private View mView; - private long mDateTaken; - private long mDateModified; + private static final String TAG = "CAM_SimpleViewData"; + + private final int mWidth; + private final int mHeight; + private final View mView; + private final long mDateTaken; + private final long mDateModified; public SimpleViewData( View v, int width, int height, @@ -91,8 +94,8 @@ public class SimpleViewData implements LocalData { } @Override - public boolean refresh(ContentResolver resolver) { - return false; + public LocalData refresh(ContentResolver resolver) { + return null; } @Override @@ -165,4 +168,22 @@ public class SimpleViewData implements LocalData { public String getMimeType() { return null; } + + @Override + public void rotate90Degrees(Context context, LocalDataAdapter adapter, + int currentDataId, boolean clockwise) { + // We don't support rotation for SimpleViewData. + Log.w(TAG, "Unexpected call in rotate90Degrees()"); + return; + } + + @Override + public long getSizeInBytes() { + return 0; + } + + @Override + public long getId() { + return -1; + } } diff --git a/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java b/src/com/android/camera/exif/ByteBufferInputStream.java index 7fb9f22cc..77c846ad0 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java +++ b/src/com/android/camera/exif/ByteBufferInputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import java.io.InputStream; import java.nio.ByteBuffer; diff --git a/gallerycommon/src/com/android/gallery3d/exif/CountedDataInputStream.java b/src/com/android/camera/exif/CountedDataInputStream.java index dfd4a1a10..4232e7d99 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/CountedDataInputStream.java +++ b/src/com/android/camera/exif/CountedDataInputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import java.io.EOFException; import java.io.FilterInputStream; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifData.java b/src/com/android/camera/exif/ExifData.java index 8422382bb..ed1f3499a 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifData.java +++ b/src/com/android/camera/exif/ExifData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.util.Log; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java index a1cf0fc85..340f19e1c 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifInterface.java +++ b/src/com/android/camera/exif/ExifInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -759,7 +759,7 @@ public class ExifInterface { } InputStream is = null; try { - is = (InputStream) new BufferedInputStream(new FileInputStream(inFileName)); + is = new BufferedInputStream(new FileInputStream(inFileName)); readExif(is); } catch (IOException e) { closeSilently(is); @@ -985,7 +985,7 @@ public class ExifInterface { } OutputStream out = null; try { - out = (OutputStream) new FileOutputStream(exifOutFileName); + out = new FileOutputStream(exifOutFileName); } catch (FileNotFoundException e) { closeSilently(out); throw e; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifInvalidFormatException.java b/src/com/android/camera/exif/ExifInvalidFormatException.java index bf923ec26..2777a5133 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifInvalidFormatException.java +++ b/src/com/android/camera/exif/ExifInvalidFormatException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; public class ExifInvalidFormatException extends Exception { public ExifInvalidFormatException(String meg) { diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java b/src/com/android/camera/exif/ExifModifier.java index f00362b6b..bed038c4a 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java +++ b/src/com/android/camera/exif/ExifModifier.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.util.Log; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java b/src/com/android/camera/exif/ExifOutputStream.java index 7ca05f2e0..191e8280c 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java +++ b/src/com/android/camera/exif/ExifOutputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.util.Log; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java b/src/com/android/camera/exif/ExifParser.java index 5467d423d..766268b1c 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java +++ b/src/com/android/camera/exif/ExifParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.util.Log; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifReader.java b/src/com/android/camera/exif/ExifReader.java index 68e972fb7..757e0d329 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifReader.java +++ b/src/com/android/camera/exif/ExifReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import android.util.Log; diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java b/src/com/android/camera/exif/ExifTag.java index b8b387201..1d50316dd 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java +++ b/src/com/android/camera/exif/ExifTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import java.nio.charset.Charset; import java.text.SimpleDateFormat; diff --git a/gallerycommon/src/com/android/gallery3d/exif/IfdData.java b/src/com/android/camera/exif/IfdData.java index 093944aec..132a8ebc5 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/IfdData.java +++ b/src/com/android/camera/exif/IfdData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import java.util.HashMap; import java.util.Map; diff --git a/gallerycommon/src/com/android/gallery3d/exif/IfdId.java b/src/com/android/camera/exif/IfdId.java index 7842edbd4..9fec68874 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/IfdId.java +++ b/src/com/android/camera/exif/IfdId.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; /** * The constants of the IFD ID defined in EXIF spec. diff --git a/gallerycommon/src/com/android/gallery3d/exif/JpegHeader.java b/src/com/android/camera/exif/JpegHeader.java index e3e787eff..383617af4 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/JpegHeader.java +++ b/src/com/android/camera/exif/JpegHeader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; class JpegHeader { public static final short SOI = (short) 0xFFD8; diff --git a/gallerycommon/src/com/android/gallery3d/exif/OrderedDataOutputStream.java b/src/com/android/camera/exif/OrderedDataOutputStream.java index 428e6b9fc..1a1b31be4 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/OrderedDataOutputStream.java +++ b/src/com/android/camera/exif/OrderedDataOutputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; import java.io.FilterOutputStream; import java.io.IOException; diff --git a/gallerycommon/src/com/android/gallery3d/exif/Rational.java b/src/com/android/camera/exif/Rational.java index 591d63faf..96b5312b0 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/Rational.java +++ b/src/com/android/camera/exif/Rational.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.gallery3d.exif; +package com.android.camera.exif; /** * The rational data type of EXIF tag. Contains a pair of longs representing the diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index 3d2e94f63..0c7aa9a0f 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -862,8 +862,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } int currentViewCenter = currentItem.getCenterX(); if (mCenterX != currentViewCenter) { - int snapInTime = (int) (SNAP_IN_CENTER_TIME_MS - * Math.abs(mCenterX - currentViewCenter) / mDrawArea.width()); + int snapInTime = SNAP_IN_CENTER_TIME_MS + * Math.abs(mCenterX - currentViewCenter) / mDrawArea.width(); mController.scrollToPosition(currentViewCenter, snapInTime, false); } |