OSDN Git Service

Hold in-progress sessions in mem, not media store
authorSeth Raphael <magicseth@google.com>
Thu, 13 Feb 2014 23:10:06 +0000 (15:10 -0800)
committerSeth Raphael <magicseth@google.com>
Mon, 10 Mar 2014 19:41:56 +0000 (12:41 -0700)
Bug: 12457236

Change-Id: I88f7c3a822010744881b8be966adbcf2774a8115

12 files changed:
src/com/android/camera/CameraActivity.java
src/com/android/camera/Storage.java
src/com/android/camera/data/AbstractLocalDataAdapterWrapper.java
src/com/android/camera/data/CameraDataAdapter.java
src/com/android/camera/data/LocalDataAdapter.java
src/com/android/camera/data/LocalDataUtil.java
src/com/android/camera/data/LocalMediaData.java
src/com/android/camera/data/LocalSessionData.java [moved from src/com/android/camera/data/InProgressDataWrapper.java with 52% similarity]
src/com/android/camera/session/CaptureSessionManagerImpl.java
src/com/android/camera/session/PlaceholderManager.java
src/com/android/camera/util/FileUtil.java
src/com/android/camera/widget/FilmstripView.java

index 4308cc8..651d4a3 100644 (file)
@@ -81,7 +81,6 @@ import com.android.camera.app.OrientationManager;
 import com.android.camera.app.OrientationManagerImpl;
 import com.android.camera.data.CameraDataAdapter;
 import com.android.camera.data.FixedLastDataAdapter;
-import com.android.camera.data.InProgressDataWrapper;
 import com.android.camera.data.LocalData;
 import com.android.camera.data.LocalDataAdapter;
 import com.android.camera.data.LocalDataUtil;
@@ -750,27 +749,12 @@ public class CameraActivity extends Activity
                 @Override
                 public void onSessionQueued(final Uri uri) {
                     notifyNewMedia(uri);
-                    int dataId = mDataAdapter.findDataByContentUri(uri);
-                    if (dataId != -1) {
-                        // Don't allow special UI actions (swipe to
-                        // delete, for example) on in-progress data.
-                        LocalData d = mDataAdapter.getLocalData(dataId);
-                        InProgressDataWrapper newData = new InProgressDataWrapper(d);
-                        mDataAdapter.updateData(dataId, newData);
-                    }
                 }
 
                 @Override
                 public void onSessionDone(final Uri uri) {
                     Log.v(TAG, "onSessionDone:" + uri);
-                    int doneId = mDataAdapter.findDataByContentUri(uri);
-                    int currentDataId = mFilmstripController.getCurrentId();
-
-                    if (currentDataId == doneId) {
-                        hideSessionProgress();
-                        updateSessionProgress(0);
-                    }
-                    mDataAdapter.refresh(uri, /* isInProgress */false);
+                    mDataAdapter.finishSession(uri);
                 }
 
                 @Override
@@ -791,7 +775,7 @@ public class CameraActivity extends Activity
 
                 @Override
                 public void onSessionUpdated(Uri uri) {
-                    mDataAdapter.refresh(uri, /* isInProgress */true);
+                    mDataAdapter.refresh(uri);
                 }
 
                 @Override
@@ -805,7 +789,8 @@ public class CameraActivity extends Activity
                         updateSessionProgress(0);
                         showProcessError(reason);
                     }
-                    mDataAdapter.refresh(uri, /* isInProgress */false);
+                    // HERE
+                    mDataAdapter.refresh(uri);
                 }
             };
 
@@ -1025,19 +1010,22 @@ public class CameraActivity extends Activity
 
     @Override
     public void notifyNewMedia(Uri uri) {
-        ContentResolver cr = getContentResolver();
-        String mimeType = cr.getType(uri);
-        if (LocalDataUtil.isMimeTypeVideo(mimeType)) {
-            sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
-            mDataAdapter.addNewVideo(uri);
-        } else if (LocalDataUtil.isMimeTypeImage(mimeType)) {
-            CameraUtil.broadcastNewPicture(mAppContext, uri);
-            mDataAdapter.addNewPhoto(uri);
-        } else if (LocalDataUtil.isMimeTypePlaceHolder(mimeType)) {
-            mDataAdapter.addNewPhoto(uri);
+        if (Storage.isSessionUri(uri)) {
+            mDataAdapter.addNewSession(uri);
         } else {
-            android.util.Log.w(TAG, "Unknown new media with MIME type:"
-                    + mimeType + ", uri:" + uri);
+
+            ContentResolver cr = getContentResolver();
+            String mimeType = cr.getType(uri);
+            if (LocalDataUtil.isMimeTypeVideo(mimeType)) {
+                sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
+                mDataAdapter.addNewVideo(uri);
+            } else if (LocalDataUtil.isMimeTypeImage(mimeType)) {
+                CameraUtil.broadcastNewPicture(mAppContext, uri);
+                mDataAdapter.addNewPhoto(uri);
+            } else {
+                android.util.Log.w(TAG, "Unknown new media with MIME type:"
+                        + mimeType + ", uri:" + uri);
+            }
         }
     }
 
@@ -1423,7 +1411,7 @@ public class CameraActivity extends Activity
         } else {
             LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId());
             if (data != null) {
-                mDataAdapter.refresh(data.getContentUri(), false);
+                mDataAdapter.refresh(data.getContentUri());
             }
         }
         // The share button might be disabled to avoid double tapping.
index 4e7d66e..f5aaf94 100644 (file)
@@ -19,6 +19,7 @@ package com.android.camera;
 import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.graphics.Point;
 import android.location.Location;
 import android.net.Uri;
 import android.os.Build;
@@ -36,24 +37,27 @@ import com.android.camera.util.ApiHelper;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.util.HashMap;
+import java.util.UUID;
 
 public class Storage {
-    private static final String TAG = "CameraStorage";
-
     public static final String DCIM =
             Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
-
     public static final String DIRECTORY = DCIM + "/Camera";
     public static final String JPEG_POSTFIX = ".jpg";
-
     // Match the code in MediaProvider.computeBucketValues().
     public static final String BUCKET_ID =
             String.valueOf(DIRECTORY.toLowerCase().hashCode());
-
     public static final long UNAVAILABLE = -1L;
     public static final long PREPARING = -2L;
     public static final long UNKNOWN_SIZE = -3L;
     public static final long LOW_STORAGE_THRESHOLD_BYTES = 50000000;
+    public static final String CAMERA_SESSION_SCHEME = "camera_session";
+    private static final String TAG = "Storage";
+    private static final String GOOGLE_COM = "google.com";
+    private static HashMap<Uri, Uri> sSessionsToContentUris = new HashMap<Uri, Uri>();
+    private static HashMap<Uri, byte[]> sSessionsToPlaceholderBytes = new HashMap<Uri, byte[]>();
+    private static HashMap<Uri, Point> sSessionsToSizes= new HashMap<Uri, Point>();
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
     private static void setImageSize(ContentValues values, int width, int height) {
@@ -136,6 +140,38 @@ public class Storage {
         return values;
     }
 
+    /**
+     * Add a placeholder for a new image that does not exist yet.
+     * @param jpeg the bytes of the placeholder image
+     * @param width the image's width
+     * @param height the image's height
+     * @return A new URI used to reference this placeholder
+     */
+    public static Uri addPlaceholder(byte[] jpeg, int width, int height) {
+        Uri uri;
+        Uri.Builder builder = new Uri.Builder();
+        String uuid = UUID.randomUUID().toString();
+        builder.scheme(CAMERA_SESSION_SCHEME).authority(GOOGLE_COM).appendPath(uuid);
+        uri = builder.build();
+
+        replacePlaceholder(uri, jpeg, width, height);
+        return uri;
+    }
+
+    /**
+     * Add or replace placeholder for a new image that does not exist yet.
+     * @param uri the uri of the placeholder to replace, or null if this is a new one
+     * @param jpeg the bytes of the placeholder image
+     * @param width the image's width
+     * @param height the image's height
+     * @return A URI used to reference this placeholder
+     */
+    public static void replacePlaceholder(Uri uri, byte[] jpeg, int width, int height) {
+        Point size = new Point(width, height);
+        sSessionsToSizes.put(uri, size);
+        sSessionsToPlaceholderBytes.put(uri, jpeg);
+    }
+
     // Add the image to media store.
     public static Uri addImage(ContentResolver resolver, String title,
             long date, Location location, int orientation, int jpegLength,
@@ -160,17 +196,35 @@ public class Storage {
     }
 
     // Overwrites the file and updates the MediaStore
-    public static void updateImage(Uri imageUri, ContentResolver resolver, String title, long date,
-            Location location, int orientation, ExifInterface exif, byte[] jpeg, int width,
-            int height, String mimeType) {
+
+    /**
+     * Take jpeg bytes and add them to the media store, either replacing an existing item
+     * or a placeholder uri to replace
+     * @param imageUri The content uri or session uri of the image being updated
+     * @param resolver The content resolver to use
+     * @param title of the image
+     * @param date of the image
+     * @param location of the image
+     * @param orientation of the image
+     * @param exif of the image
+     * @param jpeg bytes of the image
+     * @param width of the image
+     * @param height of the image
+     * @param mimeType of the image
+     * @return The content uri of the newly inserted or replaced item.
+     */
+    public static Uri updateImage(Uri imageUri, ContentResolver resolver, String title, long date,
+           Location location, int orientation, ExifInterface exif,
+           byte[] jpeg, int width, int height, String mimeType) {
         String path = generateFilepath(title);
         writeFile(path, jpeg, exif);
-        updateImage(imageUri, resolver, title, date, location, orientation, jpeg.length, path,
+        return updateImage(imageUri, resolver, title, date, location, orientation, jpeg.length, path,
                 width, height, mimeType);
     }
 
+
     // Updates the image values in MediaStore
-    public static void updateImage(Uri imageUri, ContentResolver resolver, String title,
+    private static Uri updateImage(Uri imageUri, ContentResolver resolver, String title,
             long date, Location location, int orientation, int jpegLength,
             String path, int width, int height, String mimeType) {
 
@@ -178,13 +232,23 @@ public class Storage {
                 getContentValuesForData(title, date, location, orientation, jpegLength, path,
                         width, height, mimeType);
 
-        // Update the MediaStore
-        int rowsModified = resolver.update(imageUri, values, null, null);
-        if (rowsModified != 1) {
-            // This should never happen
-            throw new IllegalStateException("Bad number of rows (" + rowsModified
-                    + ") updated for uri: " + imageUri);
+
+        Uri resultUri = imageUri;
+        if (Storage.isSessionUri(imageUri)) {
+            // If this is a session uri, then we need to add the image
+            resultUri = addImage(resolver, title, date, location, orientation, jpegLength, path,
+                    width, height, mimeType);
+            sSessionsToContentUris.put(imageUri, resultUri);
+        } else {
+            // Update the MediaStore
+            int rowsModified = resolver.update(imageUri, values, null, null);
+            if (rowsModified != 1) {
+                // This should never happen
+                throw new IllegalStateException("Bad number of rows (" + rowsModified
+                        + ") updated for uri: " + imageUri);
+            }
         }
+        return resultUri;
     }
 
     /**
@@ -219,7 +283,7 @@ public class Storage {
      * switching an image to an in-progress type for re-processing.
      *
      * @param uri the URI of the item to change
-     * @param mimeeType the new mime type of the item
+     * @param mimeType the new mime type of the item
      */
     public static void updateItemMimeType(Uri uri, String mimeType, ContentResolver resolver) {
         ContentValues values = new ContentValues(1);
@@ -246,6 +310,46 @@ public class Storage {
         return DIRECTORY + '/' + title + ".jpg";
     }
 
+    /**
+     * Returns the jpeg bytes for a placeholder session
+     *
+     * @param uri the session uri to look up
+     * @return The jpeg bytes or null
+     */
+    public static byte[] getJpegForSession(Uri uri) {
+        return sSessionsToPlaceholderBytes.get(uri);
+    }
+
+    /**
+     * Returns the dimensions of the placeholder image
+     *
+     * @param uri the session uri to look up
+     * @return The size
+     */
+    public static Point getSizeForSession(Uri uri) {
+        return sSessionsToSizes.get(uri);
+    }
+
+    /**
+     * Takes a session URI and returns the finished image's content URI
+     *
+     * @param uri the uri of the session that was replaced
+     * @return The uri of the new media item, if it exists, or null.
+     */
+    public static Uri getContentUriForSessionUri(Uri uri) {
+        return sSessionsToContentUris.get(uri);
+    }
+
+    /**
+     * Determines if a URI points to a camera session
+     *
+     * @param uri the uri to check
+     * @return true if it is a session uri.
+     */
+    public static boolean isSessionUri(Uri uri) {
+        return uri.getScheme().equals(CAMERA_SESSION_SCHEME);
+    }
+
     public static long getAvailableSpace() {
         String state = Environment.getExternalStorageState();
         Log.d(TAG, "External storage state=" + state);
@@ -281,4 +385,5 @@ public class Storage {
             Log.e(TAG, "Failed to create " + nnnAAAAA.getPath());
         }
     }
+
 }
index 4f47462..26ef0c5 100644 (file)
@@ -82,6 +82,11 @@ public abstract class AbstractLocalDataAdapterWrapper implements LocalDataAdapte
     }
 
     @Override
+    public void addNewSession(Uri uri) {
+        mAdapter.addNewSession(uri);
+    }
+
+    @Override
     public void insertData(LocalData data) {
         mAdapter.insertData(data);
     }
@@ -102,8 +107,12 @@ public abstract class AbstractLocalDataAdapterWrapper implements LocalDataAdapte
     }
 
     @Override
-    public void refresh(Uri uri, boolean isInProgressSession) {
-        mAdapter.refresh(uri, isInProgressSession);
+    public void finishSession(Uri uri) {
+        mAdapter.finishSession(uri);
+    }
+    @Override
+    public void refresh(Uri uri) {
+        mAdapter.refresh(uri);
     }
 
     @Override
index 6428176..de098ca 100644 (file)
@@ -179,9 +179,7 @@ public class CameraDataAdapter implements LocalDataAdapter {
         }
     }
 
-    // TODO: put the database query on background thread
-    @Override
-    public void addNewPhoto(Uri uri) {
+    private LocalData localDataFromUri(Uri uri) {
         Cursor cursor = mContext.getContentResolver().query(uri,
                 LocalMediaData.PhotoData.QUERY_PROJECTION,
                 MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
@@ -190,7 +188,7 @@ public class CameraDataAdapter implements LocalDataAdapter {
 
         try {
             if (cursor == null || !cursor.moveToFirst()) {
-                return;
+                return null;
             }
             newData = LocalMediaData.PhotoData.buildFromCursor(mContext, cursor);
         } finally {
@@ -199,6 +197,23 @@ public class CameraDataAdapter implements LocalDataAdapter {
                 cursor.close();
             }
         }
+        return newData;
+    }
+
+    // TODO: put the database query on background thread
+    @Override
+    public void addNewPhoto(Uri uri) {
+        LocalData newData = localDataFromUri(uri);
+        addData(uri, newData);
+    }
+
+    @Override
+    public void addNewSession(Uri uri) {
+        LocalSessionData newData = new LocalSessionData(uri);
+        addData(uri, newData);
+    }
+
+    private void addData(Uri uri, LocalData newData) {
         int pos = findDataByContentUri(uri);
         if (pos != -1) {
             // a duplicate one, just do a substitute.
@@ -243,8 +258,23 @@ public class CameraDataAdapter implements LocalDataAdapter {
     }
 
     @Override
-    public void refresh(Uri contentUri, boolean isInProgressSession) {
-        final int pos = findDataByContentUri(contentUri);
+    public void finishSession(Uri sessionUri) {
+        Uri contentUri = Storage.getContentUriForSessionUri(sessionUri);
+        if (contentUri == null) {
+            refresh(sessionUri);
+            return;
+        }
+        final int pos = findDataByContentUri(sessionUri);
+        if (pos == -1) {
+            throw new IllegalAccessError("Finishing invalid uri");
+        }
+        LocalData newData = localDataFromUri(contentUri);
+        updateData(pos, newData);
+    }
+
+    @Override
+    public void refresh(Uri uri) {
+        final int pos = findDataByContentUri(uri);
         if (pos == -1) {
             return;
         }
@@ -257,12 +287,6 @@ public class CameraDataAdapter implements LocalDataAdapter {
             mListener.onDataRemoved(pos, data);
             return;
         }
-
-        // Wrap the data item if this represents a session that is in progress.
-        if (isInProgressSession) {
-            refreshedData = new InProgressDataWrapper(refreshedData);
-        }
-
         updateData(pos, refreshedData);
     }
 
@@ -335,11 +359,7 @@ public class CameraDataAdapter implements LocalDataAdapter {
                 while (true) {
                     LocalData data = LocalMediaData.PhotoData.buildFromCursor(mContext, c);
                     if (data != null) {
-                        if (data.getMimeType().equals(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
-                            l.add(new InProgressDataWrapper(data));
-                        } else {
-                            l.add(data);
-                        }
+                        l.add(data);
                     } else {
                         Log.e(TAG, "Error loading data:"
                                 + c.getString(LocalMediaData.PhotoData.COL_DATA));
index f971c80..8472a66 100644 (file)
@@ -81,13 +81,25 @@ public interface LocalDataAdapter extends DataAdapter {
     public void addNewPhoto(Uri uri);
 
     /**
+     * Adds new placeholder uri.
+     *
+     * @param uri the uri of the session to find the placeholder images
+     */
+    public void addNewSession(Uri uri);
+
+    /**
+     * Called when a session is done processing
+     *
+     * @param uri {@link Uri} of the session.
+     */
+    public void finishSession(Uri uri);
+
+    /**
      * Refresh the data by {@link Uri}.
      *
      * @param uri The {@link Uri} of the data to refresh.
-     * @param isInProgressSession Whether this data item has a session in
-     *            progress associated with it.
      */
-    public void refresh(Uri uri, boolean isInProgressSession);
+    public void refresh(Uri uri);
 
     /**
      * Finds the {@link LocalData} of the specified content Uri.
index c51dc11..f2df1ee 100644 (file)
@@ -53,15 +53,6 @@ public class LocalDataUtil {
     }
 
     /**
-     * @param mimeType The MIME type to check.
-     * @return Whether the MIME is a placeholder type.
-     * @see {@link com.android.camera.session.PlaceholderManager}.
-     */
-    public static boolean isMimeTypePlaceHolder(String mimeType) {
-        return mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE);
-    }
-
-    /**
      * Decodes the dimension of a bitmap.
      *
      * @param path The path to the bitmap.
index d44c2a5..ee18c64 100644 (file)
@@ -528,7 +528,7 @@ public abstract class LocalMediaData implements LocalData {
             protected void onPostExecute(Bitmap bitmap) {
                 super.onPostExecute(bitmap);
                 if (mNeedsRefresh && mAdapter != null) {
-                    mAdapter.refresh(getContentUri(), mIsInProgressSession);
+                    mAdapter.refresh(getContentUri());
                 }
             }
         }
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2014 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.
 package com.android.camera.data;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.FrameLayout;
+import android.widget.ImageView;
 
-import com.android.camera2.R;
+import com.android.camera.Storage;
+
+import java.util.Date;
 
 /**
- * A wrapper class for in-progress data. Data that's still being processed
- * should not supporting any actions. Only methods related to actions like
- * {@link #isDataActionSupported(int)} and
- * {@link #isUIActionSupported(int)} are implemented by this class.
+ * This is used to represent a local data item that is in progress and not
+ * yet in the media store.
  */
-public class InProgressDataWrapper implements LocalData {
+public class LocalSessionData implements LocalData {
 
-    final LocalData mLocalData;
+    private Uri mUri;
+    private long mDateTaken;
+    protected final Bundle mMetaData;
+    private int mWidth;
+    private int mHeight;
 
-    public InProgressDataWrapper(LocalData wrappedData) {
-        mLocalData = wrappedData;
+    public LocalSessionData(Uri uri) {
+        mUri = uri;
+        mMetaData = new Bundle();
+        mDateTaken = new Date().getTime();
+        Point size = Storage.getSizeForSession(uri);
+        mWidth = size.x;
+        mHeight = size.y;
     }
 
     @Override
-    public View getView(
-            Context context, int width, int height,
-            Drawable placeHolder, LocalDataAdapter adapter, boolean isInProgress) {
-
-        return mLocalData.getView(context, width, height, placeHolder, adapter, true);
+    public View getView(Context context, int width, int height, Drawable placeHolder,
+           LocalDataAdapter adapter, boolean isInProgress) {
+        //TODO do this on a background thread
+        byte[] jpegData = Storage.getJpegForSession(mUri);
+        Bitmap bmp = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
+        ImageView imageView = new ImageView(context);
+        imageView.setImageBitmap(bmp);
+        return imageView;
     }
 
     @Override
-    public void resizeView(Context context, int w, int h, View v, LocalDataAdapter adapter) {
-        // do nothing.
+    public void resizeView(Context context, int width, int height, View view,
+           LocalDataAdapter adapter) {
+
     }
 
     @Override
     public long getDateTaken() {
-        return mLocalData.getDateTaken();
+        return mDateTaken;
     }
 
     @Override
     public long getDateModified() {
-        return mLocalData.getDateModified();
+        return mDateTaken;
     }
 
     @Override
     public String getTitle() {
-        return mLocalData.getTitle();
+        return mUri.toString();
     }
 
+
     @Override
     public boolean isDataActionSupported(int actions) {
         return false;
@@ -75,97 +91,92 @@ public class InProgressDataWrapper implements LocalData {
 
     @Override
     public boolean delete(Context c) {
-        // No actions are allowed to modify the wrapped data.
         return false;
     }
 
     @Override
-    public boolean rotate90Degrees(
-            Context context, LocalDataAdapter adapter,
-            int currentDataId, boolean clockwise) {
-        // No actions are allowed to modify the wrapped data.
+    public boolean rotate90Degrees(Context context, LocalDataAdapter adapter, int currentDataId, boolean clockwise) {
         return false;
     }
 
     @Override
     public void onFullScreen(boolean fullScreen) {
-        mLocalData.onFullScreen(fullScreen);
+
     }
 
     @Override
     public boolean canSwipeInFullScreen() {
-        return mLocalData.canSwipeInFullScreen();
+        return true;
     }
 
     @Override
     public String getPath() {
-        return mLocalData.getPath();
+        return "";
     }
 
     @Override
     public String getMimeType() {
-        return mLocalData.getMimeType();
+        return null;
     }
 
     @Override
     public MediaDetails getMediaDetails(Context context) {
-        return mLocalData.getMediaDetails(context);
+        return null;
     }
 
     @Override
     public int getLocalDataType() {
-        // Force the data type to be in-progress data.
         return LOCAL_IN_PROGRESS_DATA;
     }
 
     @Override
     public long getSizeInBytes() {
-        return mLocalData.getSizeInBytes();
+        return 0;
     }
 
     @Override
     public LocalData refresh(Context context) {
-        return mLocalData.refresh(context);
+        return this;
     }
 
     @Override
     public long getContentId() {
-        return mLocalData.getContentId();
+        return 0;
     }
 
     @Override
     public Bundle getMetadata() {
-        return mLocalData.getMetadata();
+        return mMetaData;
     }
 
     @Override
     public boolean isMetadataUpdated() {
-        return mLocalData.isMetadataUpdated();
+        return true;
     }
 
     @Override
-    public int getWidth() {
-        return mLocalData.getWidth();
+    public int getRotation() {
+        return 0;
     }
 
     @Override
-    public int getHeight() {
-        return mLocalData.getHeight();
+    public int getWidth() {
+        return mWidth;
     }
 
     @Override
-    public int getRotation() {
-        return mLocalData.getRotation();
+    public int getHeight() {
+        return mHeight;
     }
 
     @Override
     public int getViewType() {
-        return mLocalData.getViewType();
+        return VIEW_TYPE_REMOVABLE;
     }
 
     @Override
     public double[] getLatLong() {
-        return mLocalData.getLatLong();
+        return null;
     }
 
     @Override
@@ -175,16 +186,16 @@ public class InProgressDataWrapper implements LocalData {
 
     @Override
     public void prepare() {
-        mLocalData.prepare();
+
     }
 
     @Override
     public void recycle() {
-        mLocalData.recycle();
+
     }
 
     @Override
     public Uri getContentUri() {
-        return mLocalData.getContentUri();
+        return mUri;
     }
 }
index 9277d44..10a4e44 100644 (file)
 package com.android.camera.session;
 
 import android.content.ContentResolver;
+import android.graphics.BitmapFactory;
 import android.location.Location;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 
+import com.android.camera.Exif;
 import com.android.camera.app.MediaSaver;
 import com.android.camera.app.MediaSaver.OnMediaSavedListener;
-import com.android.camera.crop.ImageLoader;
 import com.android.camera.data.LocalData;
 import com.android.camera.exif.ExifInterface;
+import com.android.camera.exif.ExifTag;
+import com.android.camera.exif.Rational;
+import com.android.camera.util.FileUtil;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
@@ -39,6 +48,9 @@ import java.util.Map;
  */
 public class CaptureSessionManagerImpl implements CaptureSessionManager {
 
+    private static final String TAG = "CaptureSessionManagerImpl";
+    public static final String TEMP_SESSIONS = "TEMP_SESSIONS";
+
     private class CaptureSessionImpl implements CaptureSession {
         /** A URI of the item being processed. */
         private Uri mUri;
@@ -151,8 +163,8 @@ public class CaptureSessionManagerImpl implements CaptureSessionManager {
             }
 
             // TODO: This needs to happen outside the UI thread.
-            mPlaceholderManager.replacePlaceholder(mPlaceHolderSession, mLocation, orientation,
-                    exif, data, width, height, LocalData.MIME_TYPE_JPEG);
+            mPlaceholderManager.finishPlaceholder(mPlaceHolderSession, mLocation, orientation, exif,
+                    data, width, height, LocalData.MIME_TYPE_JPEG);
 
             mNotificationManager.notifyCompletion(mNotificationId);
             removeSession(mUri.toString());
@@ -166,12 +178,43 @@ public class CaptureSessionManagerImpl implements CaptureSessionManager {
                         "Cannot call finish without calling startSession first.");
             }
 
-            // Set final values in media store, such as mime type and size.
-            mPlaceholderManager.replacePlaceHolder(mPlaceHolderSession, mLocation,
-                    LocalData.MIME_TYPE_JPEG, /* finalImage */ true);
-            mNotificationManager.notifyCompletion(mNotificationId);
-            removeSession(mUri.toString());
-            notifyTaskDone(mPlaceHolderSession.outputUri);
+            final String path = this.getPath();
+
+            AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
+                @Override
+                public void run() {
+                    byte[] jpegDataTemp;
+                    try {
+                        jpegDataTemp = FileUtil.readFileToByteArray(new File(path));
+                    } catch (IOException e) {
+                        return;
+                    }
+                    final byte[] jpegData = jpegDataTemp;
+
+                    final CaptureSession session = CaptureSessionImpl.this;
+
+                    if (session == null) {
+                        throw new IllegalStateException("No session for captured photo");
+                    }
+                    BitmapFactory.Options options = new BitmapFactory.Options();
+                    options.inJustDecodeBounds = true;
+                    BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options);
+                    int width = options.outWidth;
+                    int height = options.outHeight;
+                    int rotation = 0;
+                    ExifInterface exif = null;
+                    try {
+                        exif = new ExifInterface();
+                        exif.readExif(jpegData);
+                    } catch (IOException e) {
+                        Log.w(TAG, "Could not read exif", e);
+                        exif = null;
+                    }
+
+                    session.saveAndFinish(jpegData, width, height, rotation, exif, null);
+                }
+            });
+
         }
 
         @Override
@@ -179,7 +222,26 @@ public class CaptureSessionManagerImpl implements CaptureSessionManager {
             if (mUri == null) {
                 throw new IllegalStateException("Cannot retrieve URI of not started session.");
             }
-            return ImageLoader.getLocalPathFromUri(mContentResolver, mUri);
+
+            File tempDirectory = null;
+            try {
+                tempDirectory = new File(
+                        getSessionDirectory(TEMP_SESSIONS), mTitle);
+            } catch (IOException e) {
+                Log.e(TAG, "Could not get temp session directory", e);
+                throw new RuntimeException("Could not get temp session directory", e);
+            }
+            tempDirectory.mkdirs();
+            File tempFile = new File(tempDirectory, mTitle  + ".jpg");
+            try {
+                if (!tempFile.exists()) {
+                    tempFile.createNewFile();
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Could not create temp session file", e);
+                throw new RuntimeException("Could not create temp session file", e);
+            }
+            return tempFile.getPath();
         }
 
         @Override
@@ -194,9 +256,32 @@ public class CaptureSessionManagerImpl implements CaptureSessionManager {
 
         @Override
         public void onPreviewChanged() {
-            mPlaceholderManager.replacePlaceHolder(mPlaceHolderSession, mLocation,
-                    PlaceholderManager.PLACEHOLDER_MIME_TYPE, /* finalImage */ false);
-            notifySessionUpdate(mPlaceHolderSession.outputUri);
+
+            final Location loc = null; // mLocationManager.getCurrentLocation();
+            final int heading = 0; // mHeading;
+            final String path = this.getPath();
+
+            AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
+                @Override
+                public void run() {
+                    byte[] jpegDataTemp;
+                    try {
+                        jpegDataTemp = FileUtil.readFileToByteArray(new File(path));
+                    } catch (IOException e) {
+                        return;
+                    }
+                    final byte[] jpegData = jpegDataTemp;
+
+                    BitmapFactory.Options options = new BitmapFactory.Options();
+                    options.inJustDecodeBounds = true;
+                    BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options);
+                    int width = options.outWidth;
+                    int height = options.outHeight;
+
+                    mPlaceholderManager.replacePlaceholder(mPlaceHolderSession, jpegData, width, height);
+                    notifySessionUpdate(mPlaceHolderSession.outputUri);
+                }
+            });
         }
 
         @Override
@@ -207,9 +292,6 @@ public class CaptureSessionManagerImpl implements CaptureSessionManager {
             }
             mProgressMessage = reason;
 
-            // Change mime type of session so it's not marked as in progress anymore.
-            mPlaceholderManager.replacePlaceHolder(mPlaceHolderSession, mLocation,
-                    LocalData.MIME_TYPE_JPEG, /* finalImage */false);
             mNotificationManager.notifyCompletion(mNotificationId);
             removeSession(mUri.toString());
             mFailedSessionMessages.put(mPlaceHolderSession.outputUri, reason);
index 5034d40..91e0760 100644 (file)
@@ -35,7 +35,6 @@ import com.android.camera.util.CameraUtil;
 public class PlaceholderManager {
     private static final String TAG = "PlaceholderManager";
 
-    public static final String PLACEHOLDER_MIME_TYPE = "application/placeholder-image";
     private final Context mContext;
 
     public static class Session {
@@ -71,9 +70,7 @@ public class PlaceholderManager {
         }
 
         Uri uri =
-                Storage.addImage(mContext.getContentResolver(), title, timestamp, null, 0, null,
-                        placeholder, width, height, PLACEHOLDER_MIME_TYPE);
-
+                Storage.addPlaceholder(placeholder, width, height);
         if (uri == null) {
             return null;
         }
@@ -88,41 +85,43 @@ public class PlaceholderManager {
      *         session.
      */
     public Session convertToPlaceholder(Uri uri) {
-        Storage.updateItemMimeType(uri, PLACEHOLDER_MIME_TYPE, mContext.getContentResolver());
         return createSessionFromUri(uri);
     }
 
-    public void replacePlaceholder(Session session, Location location, int orientation,
-            ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) {
+    /**
+     * This converts the placeholder in to a real media item
+     *
+     * @param session the session that is being finished.
+     * @param location the location of the image
+     * @param orientation the orientation of the image
+     * @param exif the exif of the image
+     * @param jpeg the bytes of the image
+     * @param width the width of the image
+     * @param height the height of the image
+     * @param mimeType the mime type of the image
+     */
+    public void finishPlaceholder(Session session, Location location, int orientation,
+                                   ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) {
 
-        Storage.updateImage(session.outputUri, mContext.getContentResolver(), session.outputTitle,
+        Uri resultUri = Storage.updateImage(session.outputUri, mContext.getContentResolver(), session.outputTitle,
                 session.time, location, orientation, exif, jpeg, width, height, mimeType);
-        CameraUtil.broadcastNewPicture(mContext, session.outputUri);
+        CameraUtil.broadcastNewPicture(mContext, resultUri);
     }
 
     /**
-     * Replace the placeholder with an updated image.
+     * This changes the temporary placeholder jpeg without writing it to the media store
      *
-     * @param session the session to update.
-     * @param loc the location of the new item.
-     * @param mimeType the mime-type of the new image.
-     * @param finalImage whether this is the final image. If set, this will
-     *            broadcast that a new picture has been added.
+     * @param session the session to update
+     * @param jpeg the new placeholder bytes
+     * @param width the width of the image
+     * @param height the height of the image
      */
-    public void replacePlaceHolder(Session session, Location loc, String mimeType,
-            boolean finalImage) {
-        Storage.updateImageFromChangedFile(session.outputUri, loc, mContext.getContentResolver(),
-                mimeType);
-        if (finalImage) {
-            CameraUtil.broadcastNewPicture(mContext, session.outputUri);
-        }
-    }
+    public void replacePlaceholder(Session session,
+                                   byte[] jpeg, int width, int height) {
 
-    /**
-     * Removes the placeholder for the given session.
-     */
-    public void removePlaceholder(Session session) {
-        Storage.deleteImage(mContext.getContentResolver(), session.outputUri);
+        Storage.replacePlaceholder(session.outputUri,
+                jpeg, width, height);
+        CameraUtil.broadcastNewPicture(mContext, session.outputUri);
     }
 
     /**
index e1fac89..3d22db1 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.camera.util;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 
 /**
  * Common file operations.
@@ -44,4 +46,29 @@ public class FileUtil {
         }
         return directory.delete();
     }
+
+    /**
+     * Reads the content of a {@code File} as a byte array.
+     *
+     * @param file The file to read
+     * @return  The content of the file
+     * @throws java.io.IOException if the content of the {@code File} could not be read
+     */
+    public static byte[] readFileToByteArray(File file) throws IOException {
+        int length = (int) file.length();
+        byte[] data = new byte[length];
+        FileInputStream stream = new FileInputStream(file);
+        try {
+            int offset = 0;
+            while (offset < length) {
+                offset += stream.read(data, offset, length - offset);
+            }
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            stream.close();
+        }
+        return data;
+    }
+
 }
index c794854..8f4a86d 100644 (file)
@@ -2518,7 +2518,7 @@ public class FilmstripView extends ViewGroup {
             if (currItem == null) {
                 return false;
             }
-            if (!mDataAdapter.canSwipeInFullScreen(currItem.getId())) {
+            if (inFullScreen() && !mDataAdapter.canSwipeInFullScreen(currItem.getId())) {
                 return false;
             }
             hideZoomView();