OSDN Git Service

Adding reporting functionality using Feedback API for PhotoModule and CaptureModule...
authorzafir <zafir@google.com>
Fri, 6 Feb 2015 03:04:38 +0000 (19:04 -0800)
committerzafir <zafir@google.com>
Wed, 25 Feb 2015 19:52:24 +0000 (11:52 -0800)
Bug: 19231841
Change-Id: I7d66db1e4c273dc7ec082f40a80b349bd91a7e8b

13 files changed:
res/values/strings.xml
src/com/android/camera/CameraActivity.java
src/com/android/camera/MediaSaverImpl.java
src/com/android/camera/PhotoModule.java
src/com/android/camera/Storage.java
src/com/android/camera/exif/ExifInterface.java
src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
src/com/android/camera/session/CaptureSessionImpl.java
src/com/android/camera/session/PlaceholderManager.java
src/com/android/camera/session/StackSaver.java
src/com/android/camera/session/StackSaverImpl.java
src/com/android/camera/util/CameraUtil.java
src_pd/com/android/camera/util/GoogleHelpHelper.java

index 948ae2c..d9d96a4 100644 (file)
     <!-- message for the dialog showing the camera is disabled because of security policies. Camera cannot be used. -->
     <string name="camera_disabled">Camera has been disabled because of security policies.</string>
 
+    <!-- message for the dialog showing that the user's photo could not be saved [CHAR LIMIT=NONE] -->
+    <string name="media_storage_failure">There was a problem saving your photo or video.</string>
+
+    <!-- Reason used for session failure which is visible in UI [CHAR LIMIT=NONE] -->
+    <string name="reason_storage_failure">Photo storage failure.</string>
+
     <!-- alert to the user to wait for some operation to complete -->
     <string name="wait">Please wait\u2026</string>
 
     <!-- A label that overlays on top of the preview frame to indicate the camcorder is in time lapse mode [CHAR LIMIT=35] -->
     <string name="time_lapse_title">Time lapse recording</string>
 
+    <!-- Default feedback that is entered in the Feedback textview for issues related to camera access. [CHAR LIMIT=NONE] -->
+    <string name="feedback_description_camera_access">The app couldn\'t connect to the camera</string>
+
+    <!-- Default feedback that is entered in the Feedback textview for issues related to saving photos. [CHAR LIMIT=NONE] -->
+    <string name="feedback_description_save_photo">Photo or video did not save to the device.</string>
+
     <!-- Screen display message during image capture to indicate that the capture is in progress, like during HDR+. [CHAR LIMIT=20] -->
     <string name="capturing">Capturing</string>
 
index 7e6ba79..ea1bf0b 100644 (file)
@@ -531,7 +531,7 @@ public class CameraActivity extends QuickActivity
                 eventprotos.CameraFailure.FailureReason.SECURITY, null,
                 UsageStatistics.NONE, UsageStatistics.NONE);
         Log.w(TAG, "Camera disabled: " + cameraId);
-        CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
+        CameraUtil.showError(this, R.string.camera_disabled, R.string.feedback_description_camera_access, true);
     }
 
     @Override
@@ -540,13 +540,13 @@ public class CameraActivity extends QuickActivity
                 eventprotos.CameraFailure.FailureReason.OPEN_FAILURE, info,
                 UsageStatistics.NONE, UsageStatistics.NONE);
         Log.w(TAG, "Camera open failure: " + info);
-        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
+        CameraUtil.showError(this, R.string.camera_disabled, R.string.feedback_description_camera_access, true);
     }
 
     @Override
     public void onDeviceOpenedAlready(int cameraId, String info) {
         Log.w(TAG, "Camera open already: " + cameraId + "," + info);
-        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
+        CameraUtil.showError(this, R.string.camera_disabled, R.string.feedback_description_camera_access, true);
     }
 
     @Override
@@ -555,7 +555,7 @@ public class CameraActivity extends QuickActivity
                 eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE, null,
                 UsageStatistics.NONE, UsageStatistics.NONE);
         Log.w(TAG, "Camera reconnection failure:" + info);
-        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
+        CameraUtil.showError(this, R.string.camera_disabled, R.string.feedback_description_camera_access, true);
     }
 
     private static class MainHandler extends Handler {
@@ -976,6 +976,12 @@ public class CameraActivity extends QuickActivity
                         updateSessionProgress(0);
                         showProcessError(reason);
                     }
+                    if (reason.equals("content")) {
+                        UsageStatistics.instance().storageWarning(Storage.ACCESS_FAILURE);
+                        CameraUtil.showError(CameraActivity.this, R.string.media_storage_failure,
+                                R.string.feedback_description_save_photo, false);
+                    }
+
                     // HERE
                     mDataAdapter.refresh(uri);
                 }
@@ -1390,8 +1396,8 @@ public class CameraActivity extends QuickActivity
                         Log.e(TAG, "Fatal error during onPause, call Activity.finish()");
                         finish();
                     } else {
-                        CameraUtil.showErrorAndFinish(CameraActivity.this,
-                                R.string.cannot_connect_camera);
+                        CameraUtil.showError(CameraActivity.this, R.string.camera_disabled,
+                                R.string.feedback_description_camera_access, true);
                     }
                 }
             };
@@ -1447,7 +1453,7 @@ public class CameraActivity extends QuickActivity
             // Log error and continue. Modules requiring OneCamera should check
             // and handle if null by showing error dialog or other treatment.
             Log.e(TAG, "Creating camera manager failed.", e);
-            CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
+            CameraUtil.showError(this, R.string.camera_disabled, R.string.feedback_description_camera_access, true);
         }
         profile.mark("OneCameraManager.get");
         mCameraController = new CameraController(mAppContext, this, mMainHandler,
@@ -1663,7 +1669,8 @@ public class CameraActivity extends QuickActivity
 
             @Override
             public void onCameraAccessException() {
-                CameraUtil.showErrorAndFinish(CameraActivity.this, R.string.cannot_connect_camera);
+                CameraUtil.showError(CameraActivity.this, R.string.camera_disabled,
+                        R.string.feedback_description_camera_access, true);
             }
         });
         profile.stop();
@@ -2674,7 +2681,7 @@ public class CameraActivity extends QuickActivity
 
     @Override
     public void showErrorAndFinish(int messageId) {
-        CameraUtil.showErrorAndFinish(this, messageId);
+        CameraUtil.showError(this, messageId, R.string.feedback_description_camera_access, true);
     }
 
     @Override
index 296b5bf..3107bb5 100644 (file)
@@ -30,6 +30,7 @@ import com.android.camera.debug.Log;
 import com.android.camera.exif.ExifInterface;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * A class implementing {@link com.android.camera.app.MediaSaver}.
@@ -177,14 +178,19 @@ public class MediaSaverImpl implements MediaSaver {
                 width = options.outWidth;
                 height = options.outHeight;
             }
-            return Storage.addImage(
-                    resolver, title, date, loc, orientation, exif, data, width, height,
-                    mimeType);
+            try {
+                return Storage.addImage(
+                        resolver, title, date, loc, orientation, exif, data, width, height,
+                        mimeType);
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to write data", e);
+                return null;
+            }
         }
 
         @Override
         protected void onPostExecute(Uri uri) {
-            if (listener != null && uri != null) {
+            if (listener != null) {
                 listener.onMediaSaved(uri);
             }
             boolean previouslyFull = isQueueFull();
index 13bc2fa..7a8212d 100644 (file)
@@ -95,6 +95,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
 
+
 public class PhotoModule
         extends CameraModule
         implements PhotoController,
@@ -250,13 +251,27 @@ public class PhotoModule
 
     private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
             new MediaSaver.OnMediaSavedListener() {
+
                 @Override
                 public void onMediaSaved(Uri uri) {
                     if (uri != null) {
                         mActivity.notifyNewMedia(uri);
+                    } else {
+                        onError(false);
                     }
                 }
             };
+
+    /**
+     * Displays Feedback dialog on non-fatal errors
+     * @param finishActivity indicates whether to finish the activity after the user submits Feedback
+     */
+    private void onError(boolean finishActivity) {
+        UsageStatistics.instance().storageWarning(Storage.ACCESS_FAILURE);
+        CameraUtil.showError(mActivity, R.string.media_storage_failure,
+                R.string.feedback_description_save_photo, finishActivity);
+    }
+
     private boolean mShouldResizeTo16x9 = false;
 
     /**
@@ -1247,8 +1262,7 @@ public class PhotoModule
                     mActivity.setResultEx(Activity.RESULT_OK);
                     mActivity.finish();
                 } catch (IOException ex) {
-                    Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
-                    // ignore exception
+                    onError(true);
                 } finally {
                     CameraUtil.closeSilently(outputStream);
                 }
@@ -1277,12 +1291,12 @@ public class PhotoModule
             } catch (FileNotFoundException ex) {
                 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
                 mActivity.setResultEx(Activity.RESULT_CANCELED);
-                mActivity.finish();
+                onError(true);
                 return;
             } catch (IOException ex) {
                 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
                 mActivity.setResultEx(Activity.RESULT_CANCELED);
-                mActivity.finish();
+                onError(true);
                 return;
             } finally {
                 CameraUtil.closeSilently(tempStream);
index 7cf3885..04ce734 100644 (file)
@@ -28,7 +28,6 @@ import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Images.ImageColumns;
 import android.provider.MediaStore.MediaColumns;
 import android.util.LruCache;
-
 import com.android.camera.data.FilmstripItemData;
 import com.android.camera.debug.Log;
 import com.android.camera.exif.ExifInterface;
@@ -36,14 +35,14 @@ import com.android.camera.util.ApiHelper;
 import com.android.camera.util.Size;
 import com.google.common.base.Optional;
 
+import javax.annotation.Nonnull;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.Nonnull;
-
 public class Storage {
     public static final String DCIM =
             Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
@@ -57,6 +56,7 @@ public class Storage {
     public static final long UNAVAILABLE = -1L;
     public static final long PREPARING = -2L;
     public static final long UNKNOWN_SIZE = -3L;
+    public static final long ACCESS_FAILURE = -4L;
     public static final long LOW_STORAGE_THRESHOLD_BYTES = 50000000;
     public static final String CAMERA_SESSION_SCHEME = "camera_session";
     private static final Log.Tag TAG = new Log.Tag("Storage");
@@ -91,7 +91,7 @@ public class Storage {
      */
     public static Uri addImage(ContentResolver resolver, String title, long date,
             Location location, int orientation, ExifInterface exif, byte[] jpeg, int width,
-            int height) {
+            int height) throws IOException {
 
         return addImage(resolver, title, date, location, orientation, exif, jpeg, width, height,
               FilmstripItemData.MIME_TYPE_JPEG);
@@ -120,7 +120,7 @@ public class Storage {
      */
     public static Uri addImage(ContentResolver resolver, String title, long date,
             Location location, int orientation, ExifInterface exif, byte[] data, int width,
-            int height, String mimeType) {
+            int height, String mimeType) throws IOException {
 
         String path = generateFilepath(title, mimeType);
         long fileLength = writeFile(path, data, exif);
@@ -269,7 +269,7 @@ public class Storage {
      */
     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) {
+           byte[] jpeg, int width, int height, String mimeType) throws IOException {
         String path = generateFilepath(title, mimeType);
         writeFile(path, jpeg, exif);
         return updateImage(imageUri, resolver, title, date, location, orientation, jpeg.length, path,
@@ -301,23 +301,19 @@ public class Storage {
      *
      * @return The size of the file. -1 if failed.
      */
-    public static long writeFile(String path, byte[] jpeg, ExifInterface exif) {
+    public static long writeFile(String path, byte[] jpeg, ExifInterface exif) throws IOException {
         if (!createDirectoryIfNeeded(path)) {
             Log.e(TAG, "Failed to create parent directory for file: " + path);
             return -1;
         }
         if (exif != null) {
-            try {
                 exif.writeExif(jpeg, path);
                 File f = new File(path);
                 return f.length();
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to write data", e);
-            }
         } else {
             return writeFile(path, jpeg);
         }
-        return -1;
+//        return -1;
     }
 
     /**
index 340f19e..4b6eb8a 100644 (file)
@@ -19,6 +19,7 @@ package com.android.camera.exif;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.util.SparseIntArray;
+import com.android.camera.debug.Log;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -317,6 +318,7 @@ public class ExifInterface {
     // IFD Interoperability tags
     public static final int TAG_INTEROPERABILITY_INDEX =
         defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1);
+    private static final Log.Tag TAG = new Log.Tag("ExifInterface");
 
     /**
      * Tags that contain offset markers. These are included in the banned
@@ -758,13 +760,10 @@ public class ExifInterface {
             throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
         }
         InputStream is = null;
-        try {
-            is = new BufferedInputStream(new FileInputStream(inFileName));
-            readExif(is);
-        } catch (IOException e) {
-            closeSilently(is);
-            throw e;
-        }
+
+        is = new BufferedInputStream(new FileInputStream(inFileName));
+        readExif(is);
+
         is.close();
     }
 
@@ -856,14 +855,9 @@ public class ExifInterface {
             throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
         }
         OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            s.write(jpeg, 0, jpeg.length);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
+        s = getExifWriterStream(exifOutFileName);
+        s.write(jpeg, 0, jpeg.length);
+        s.flush();
         s.close();
     }
 
@@ -883,14 +877,10 @@ public class ExifInterface {
             throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
         }
         OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
+
+        s = getExifWriterStream(exifOutFileName);
+        bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
+        s.flush();
         s.close();
     }
 
@@ -910,14 +900,11 @@ public class ExifInterface {
             throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
         }
         OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            doExifStreamIO(jpegStream, s);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
+
+        s = getExifWriterStream(exifOutFileName);
+        doExifStreamIO(jpegStream, s);
+        s.flush();
+
         s.close();
     }
 
@@ -937,13 +924,10 @@ public class ExifInterface {
             throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
         }
         InputStream is = null;
-        try {
-            is = new FileInputStream(jpegFileName);
-            writeExif(is, exifOutFileName);
-        } catch (IOException e) {
-            closeSilently(is);
-            throw e;
-        }
+
+        is = new FileInputStream(jpegFileName);
+        writeExif(is, exifOutFileName);
+
         is.close();
     }
 
@@ -1042,9 +1026,6 @@ public class ExifInterface {
             // Attempt to overwrite tag values without changing lengths (avoids
             // file copy).
             ret = rewriteExif(buf, tags);
-        } catch (IOException e) {
-            closeSilently(file);
-            throw e;
         } finally {
             closeSilently(is);
         }
@@ -1108,9 +1089,6 @@ public class ExifInterface {
                 readExif(imageBytes);
                 setTags(tags);
                 writeExif(imageBytes, filename);
-            } catch (IOException e) {
-                closeSilently(is);
-                throw e;
             } finally {
                 is.close();
                 // Prevent clobbering of mData
index f35252b..0c01cf7 100644 (file)
@@ -33,6 +33,7 @@ import com.android.camera.processing.imagebackend.ImageProcessorProxyListener;
 import com.android.camera.processing.imagebackend.ImageToProcess;
 import com.android.camera.processing.imagebackend.TaskImageContainer;
 import com.android.camera.session.CaptureSession;
+
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -176,7 +177,7 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
     private final MainThread mExecutor;
     private final ImageRotationCalculator mImageRotationCalculator;
     private final ImageBackend mImageBackend;
-
+    
     /**
      * Constructor
      *
index c99398c..1500aaf 100644 (file)
@@ -238,8 +238,15 @@ public class CaptureSessionImpl implements CaptureSession {
                     orientation, exif, listener);
             return;
         }
-        mContentUri = mPlaceholderManager.finishPlaceholder(mPlaceHolderSession, mLocation,
-                orientation, exif, data, width, height, FilmstripItemData.MIME_TYPE_JPEG);
+        try {
+            mContentUri = mPlaceholderManager.finishPlaceholder(mPlaceHolderSession, mLocation,
+                    orientation, exif, data, width, height, FilmstripItemData.MIME_TYPE_JPEG);
+        } catch (IOException e) {
+            Log.e(TAG, "IOException", e);
+            // TODO: Replace with a sensisble description
+            // Placeholder string R.string.reason_storage_failure
+            finishWithFailure("content");
+        }
 
         mSessionNotifier.notifyTaskDone(mUri);
     }
index fa19975..7ed7779 100644 (file)
@@ -32,6 +32,8 @@ import com.android.camera.util.CameraUtil;
 import com.android.camera.util.Size;
 import com.google.common.base.Optional;
 
+import java.io.IOException;
+
 /**
  * Handles placeholders in filmstrip that show up temporarily while a final
  * output media item is being produced.
@@ -131,7 +133,7 @@ public class PlaceholderManager {
      * @return The content URI of the new media item.
      */
     public Uri finishPlaceholder(Session session, Location location, int orientation,
-            ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) {
+            ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) throws IOException {
         Uri resultUri = Storage.updateImage(session.outputUri, mContext.getContentResolver(),
                 session.outputTitle, session.time, location, orientation, exif, jpeg, width,
                 height, mimeType);
index a65e9c9..17c3f38 100644 (file)
@@ -17,7 +17,6 @@
 package com.android.camera.session;
 
 import android.net.Uri;
-
 import com.android.camera.exif.ExifInterface;
 
 /**
index c7534ed..69f912a 100644 (file)
@@ -25,6 +25,7 @@ import com.android.camera.debug.Log;
 import com.android.camera.exif.ExifInterface;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Default implementation of the {@link StackSaver} interface. It creates a
@@ -60,7 +61,7 @@ public class StackSaverImpl implements StackSaver {
 
     @Override
     public Uri saveStackedImage(byte[] data, String title, int width, int height,
-            int imageOrientation, ExifInterface exif, long captureTimeEpoch, String mimeType) {
+            int imageOrientation, ExifInterface exif, long captureTimeEpoch, String mimeType)  {
         if (exif != null) {
             exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION, imageOrientation));
         }
@@ -69,7 +70,12 @@ public class StackSaverImpl implements StackSaver {
                 Storage.generateFilepath(mStackDirectory.getAbsolutePath(), title, mimeType);
         Log.d(TAG, "Saving using stack image saver: " + filePath);
 
-        long fileLength = Storage.writeFile(filePath, data, exif);
+        long fileLength = 0;
+        try {
+            fileLength = Storage.writeFile(filePath, data, exif);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
         if (fileLength >= 0) {
             return Storage.addImageToMediaStore(mContentResolver, title, captureTimeEpoch,
                     mGpsLocation, imageOrientation, fileLength, filePath, width, height, mimeType);
index 8813ca7..123878f 100644 (file)
@@ -284,12 +284,15 @@ public class CameraUtil {
      * Shows custom error dialog. Designed specifically
      * for the scenario where the camera cannot be attached.
      */
-    public static void showErrorAndFinish(final Activity activity, int msgId) {
-        DialogInterface.OnClickListener buttonListener =
+    public static void showError(final Activity activity, final int dialogMsgId, final int feedbackMsgId,
+                                 final boolean finishActivity) {
+        final DialogInterface.OnClickListener buttonListener =
                 new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        activity.finish();
+                        if (finishActivity) {
+                            activity.finish();
+                        }
                     }
                 };
 
@@ -297,8 +300,10 @@ public class CameraUtil {
                 new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        new GoogleHelpHelper(activity).sendGoogleFeedback();
-                        activity.finish();
+                        new GoogleHelpHelper(activity).sendGoogleFeedback(feedbackMsgId);
+                        if (finishActivity) {
+                            activity.finish();
+                        }
                     }
                 };
         TypedValue out = new TypedValue();
@@ -311,7 +316,7 @@ public class CameraUtil {
             new AlertDialog.Builder(activity)
                     .setCancelable(false)
                     .setTitle(R.string.camera_error_title)
-                    .setMessage(msgId)
+                    .setMessage(dialogMsgId)
                     .setNegativeButton(R.string.dialog_report, reportButtonListener)
                     .setPositiveButton(R.string.dialog_dismiss, buttonListener)
                     .setIcon(out.resourceId)
index d64d22d..c59b50a 100644 (file)
@@ -32,5 +32,5 @@ public class GoogleHelpHelper {
     public void launchGoogleHelp() {
     }
 
-    public void sendGoogleFeedback() {}
+    public void sendGoogleFeedback(int category) {}
 }