Refactor MediaSaveService for future test needs.
Added interface MediaSaver as the abstract layer to make the implementation
independent of the clients.
Change-Id: I98db3f732e4ed1c87da005b9c18e888682a5e6a5
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 52792ba..b923395 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -65,6 +65,7 @@
import com.android.camera.CameraManager.CameraOpenErrorCallback;
import com.android.camera.app.AppManagerFactory;
+import com.android.camera.app.MediaSaver;
import com.android.camera.app.PlaceholderManager;
import com.android.camera.app.PanoramaStitchingManager;
import com.android.camera.crop.CropActivity;
@@ -207,19 +208,19 @@
}
}
- private MediaSaveService mMediaSaveService;
+ private MediaSaver mMediaSaver;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder b) {
- mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
- mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
+ mMediaSaver = ((MediaSaveService.LocalBinder) b).getService();
+ mCurrentModule.onMediaSaverAvailable(mMediaSaver);
}
@Override
public void onServiceDisconnected(ComponentName className) {
- if (mMediaSaveService != null) {
- mMediaSaveService.setListener(null);
- mMediaSaveService = null;
+ if (mMediaSaver != null) {
+ mMediaSaver.setQueueListener(null);
+ mMediaSaver = null;
}
}
};
@@ -789,8 +790,8 @@
}
};
- public MediaSaveService getMediaSaveService() {
- return mMediaSaveService;
+ public MediaSaver getMediaSaver() {
+ return mMediaSaver;
}
public void notifyNewMedia(Uri uri) {
@@ -1342,8 +1343,8 @@
openModule(mCurrentModule);
mCurrentModule.onOrientationChanged(mLastRawOrientation);
- if (mMediaSaveService != null) {
- mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
+ if (mMediaSaver != null) {
+ mCurrentModule.onMediaSaverAvailable(mMediaSaver);
}
// Store the module index so we can use it the next time the Camera
diff --git a/src/com/android/camera/CameraModule.java b/src/com/android/camera/CameraModule.java
index 55cae9f..b6fb0e6 100644
--- a/src/com/android/camera/CameraModule.java
+++ b/src/com/android/camera/CameraModule.java
@@ -21,6 +21,8 @@
import android.view.KeyEvent;
import android.view.View;
+import com.android.camera.app.MediaSaver;
+
public interface CameraModule {
public void init(CameraActivity activity, View frame);
@@ -63,7 +65,7 @@
public void onShowSwitcherPopup();
- public void onMediaSaveServiceConnected(MediaSaveService s);
+ public void onMediaSaverAvailable(MediaSaver s);
public boolean arePreviewControlsVisible();
}
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
index e8ec08d..1bcbcf2 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -29,14 +29,15 @@
import android.provider.MediaStore.Video;
import android.util.Log;
+import com.android.camera.app.MediaSaver;
import com.android.camera.exif.ExifInterface;
import java.io.File;
-/*
- * Service for saving images in the background thread.
+/**
+ * A class implementing {@link com.android.camera.app.MediaSaver}.
*/
-public class MediaSaveService extends Service {
+public class MediaSaveService extends Service implements MediaSaver {
public static final String VIDEO_BASE_URI = "content://media/external/video/media";
// The memory limit for unsaved image is 20MB.
@@ -44,20 +45,12 @@
private static final String TAG = "CAM_" + MediaSaveService.class.getSimpleName();
private final IBinder mBinder = new LocalBinder();
- private Listener mListener;
+ private QueueListener mQueueListener;
// Memory used by the total queued save request, in bytes.
private long mMemoryUse;
- public interface Listener {
- public void onQueueStatus(boolean full);
- }
-
- public interface OnMediaSavedListener {
- public void onMediaSaved(Uri uri);
- }
-
class LocalBinder extends Binder {
- public MediaSaveService getService() {
+ public MediaSaver getService() {
return MediaSaveService.this;
}
}
@@ -81,13 +74,15 @@
mMemoryUse = 0;
}
+ @Override
public boolean isQueueFull() {
return (mMemoryUse >= SAVE_TASK_MEMORY_LIMIT);
}
- public void addImage(final byte[] data, String title, long date, Location loc,
- int width, int height, int orientation, ExifInterface exif,
- OnMediaSavedListener l, ContentResolver resolver) {
+ @Override
+ public void addImage(final byte[] data, String title, long date, Location loc, int width,
+ int height, int orientation, ExifInterface exif, OnMediaSavedListener l,
+ ContentResolver resolver) {
if (isQueueFull()) {
Log.e(TAG, "Cannot add image when the queue is full");
return;
@@ -103,39 +98,41 @@
t.execute();
}
- public void addImage(final byte[] data, String title, long date, Location loc,
- int orientation, ExifInterface exif,
- OnMediaSavedListener l, ContentResolver resolver) {
+ @Override
+ public void addImage(final byte[] data, String title, long date, Location loc, int orientation,
+ ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver) {
// When dimensions are unknown, pass 0 as width and height,
// and decode image for width and height later in a background thread
addImage(data, title, date, loc, 0, 0, orientation, exif, l, resolver);
}
- public void addImage(final byte[] data, String title, Location loc,
- int width, int height, int orientation, ExifInterface exif,
- OnMediaSavedListener l, ContentResolver resolver) {
+ @Override
+ public void addImage(final byte[] data, String title, Location loc, int width, int height,
+ int orientation, ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver) {
addImage(data, title, System.currentTimeMillis(), loc, width, height,
orientation, exif, l, resolver);
}
- public void addVideo(String path, long duration, ContentValues values,
- OnMediaSavedListener l, ContentResolver resolver) {
+ @Override
+ public void addVideo(String path, long duration, ContentValues values, OnMediaSavedListener l,
+ ContentResolver resolver) {
// We don't set a queue limit for video saving because the file
// is already in the storage. Only updating the database.
new VideoSaveTask(path, duration, values, l, resolver).execute();
}
- public void setListener(Listener l) {
- mListener = l;
+ @Override
+ public void setQueueListener(QueueListener l) {
+ mQueueListener = l;
if (l == null) return;
l.onQueueStatus(isQueueFull());
}
private void onQueueFull() {
- if (mListener != null) mListener.onQueueStatus(true);
+ if (mQueueListener != null) mQueueListener.onQueueStatus(true);
}
private void onQueueAvailable() {
- if (mListener != null) mListener.onQueueStatus(false);
+ if (mQueueListener != null) mQueueListener.onQueueStatus(false);
}
private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 347244e..5174825 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -58,6 +58,7 @@
import com.android.camera.CameraManager.CameraProxy;
import com.android.camera.CameraManager.CameraShutterCallback;
import com.android.camera.PhotoModule.NamedImages.NamedEntity;
+import com.android.camera.app.MediaSaver;
import com.android.camera.exif.ExifInterface;
import com.android.camera.exif.ExifTag;
import com.android.camera.exif.Rational;
@@ -83,8 +84,7 @@
PhotoController,
FocusOverlayManager.Listener,
CameraPreference.OnPreferenceChangedListener,
- ShutterButton.OnShutterButtonListener,
- MediaSaveService.Listener,
+ ShutterButton.OnShutterButtonListener, MediaSaver.QueueListener,
OnCountDownFinishedListener,
SensorEventListener {
@@ -263,8 +263,8 @@
// True if all the parameters needed to start preview is ready.
private boolean mCameraPreviewParamsReady = false;
- private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
+ private MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
+ new MediaSaver.OnMediaSavedListener() {
@Override
public void onMediaSaved(Uri uri) {
if (uri != null) {
@@ -645,11 +645,11 @@
keepMediaProviderInstance();
mUI.initializeFirstTime();
- MediaSaveService s = mActivity.getMediaSaveService();
+ MediaSaver s = mActivity.getMediaSaver();
// We set the listener only when both service and shutterbutton
// are initialized.
if (s != null) {
- s.setListener(this);
+ s.setQueueListener(this);
}
mNamedImages = new NamedImages();
@@ -667,9 +667,9 @@
boolean recordLocation = RecordLocationPreference.get(
mPreferences, mContentResolver);
mLocationManager.recordLocation(recordLocation);
- MediaSaveService s = mActivity.getMediaSaveService();
+ MediaSaver s = mActivity.getMediaSaver();
if (s != null) {
- s.setListener(this);
+ s.setQueueListener(this);
}
mNamedImages = new NamedImages();
mUI.initializeSecondTime(mParameters);
@@ -855,7 +855,7 @@
exif.setTag(directionRefTag);
exif.setTag(directionTag);
}
- mActivity.getMediaSaveService().addImage(
+ mActivity.getMediaSaver().addImage(
jpegData, title, date, mLocation, width, height,
orientation, exif, mOnMediaSavedListener, mContentResolver);
}
@@ -968,8 +968,8 @@
// is full then ignore.
if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
|| mCameraState == SWITCHING_CAMERA
- || mActivity.getMediaSaveService() == null
- || mActivity.getMediaSaveService().isQueueFull()) {
+ || mActivity.getMediaSaver() == null
+ || mActivity.getMediaSaver().isQueueFull()) {
return false;
}
mCaptureStartTime = System.currentTimeMillis();
@@ -1370,9 +1370,9 @@
mPendingSwitchCameraId = -1;
if (mFocusManager != null) mFocusManager.removeMessages();
- MediaSaveService s = mActivity.getMediaSaveService();
+ MediaSaver s = mActivity.getMediaSaver();
if (s != null) {
- s.setListener(null);
+ s.setQueueListener(null);
}
mUI.removeDisplayChangeListener();
}
@@ -2030,11 +2030,11 @@
}
@Override
- public void onMediaSaveServiceConnected(MediaSaveService s) {
+ public void onMediaSaverAvailable(MediaSaver s) {
// We set the listener only when both service and shutterbutton
// are initialized.
if (mFirstTimeInitialized) {
- s.setListener(this);
+ s.setQueueListener(this);
}
}
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 0ee62bb..1159879 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -55,6 +55,7 @@
import com.android.camera.CameraManager.CameraPictureCallback;
import com.android.camera.CameraManager.CameraProxy;
+import com.android.camera.app.MediaSaver;
import com.android.camera.app.OrientationManager;
import com.android.camera.exif.ExifInterface;
import com.android.camera.ui.RotateTextToast;
@@ -172,8 +173,8 @@
private int mZoomValue; // The current zoom value.
- private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
+ private final MediaSaver.OnMediaSavedListener mOnVideoSavedListener =
+ new MediaSaver.OnMediaSavedListener() {
@Override
public void onMediaSaved(Uri uri) {
if (uri != null) {
@@ -185,8 +186,8 @@
}
};
- private final MediaSaveService.OnMediaSavedListener mOnPhotoSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
+ private final MediaSaver.OnMediaSavedListener mOnPhotoSavedListener =
+ new MediaSaver.OnMediaSavedListener() {
@Override
public void onMediaSaved(Uri uri) {
if (uri != null) {
@@ -387,7 +388,7 @@
if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress) {
return;
}
- MediaSaveService s = mActivity.getMediaSaveService();
+ MediaSaver s = mActivity.getMediaSaver();
if (s == null || s.isQueueFull()) {
return;
}
@@ -1095,7 +1096,7 @@
} else {
Log.w(TAG, "Video duration <= 0 : " + duration);
}
- mActivity.getMediaSaveService().addVideo(mCurrentVideoFilename,
+ mActivity.getMediaSaver().addVideo(mCurrentVideoFilename,
duration, mCurrentVideoValues,
mOnVideoSavedListener, mContentResolver);
}
@@ -1731,7 +1732,7 @@
ExifInterface exif = Exif.getExif(data);
int orientation = Exif.getOrientation(exif);
- mActivity.getMediaSaveService().addImage(
+ mActivity.getMediaSaver().addImage(
data, title, dateTaken, loc, orientation,
exif, mOnPhotoSavedListener, mContentResolver);
}
@@ -1797,7 +1798,7 @@
}
@Override
- public void onMediaSaveServiceConnected(MediaSaveService s) {
+ public void onMediaSaverAvailable(MediaSaver s) {
// do nothing.
}
diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java
index 8d00c70..e29fcb6 100644
--- a/src/com/android/camera/WideAnglePanoramaModule.java
+++ b/src/com/android/camera/WideAnglePanoramaModule.java
@@ -45,6 +45,7 @@
import android.view.WindowManager;
import com.android.camera.CameraManager.CameraProxy;
+import com.android.camera.app.MediaSaver;
import com.android.camera.app.OrientationManager;
import com.android.camera.data.LocalData;
import com.android.camera.exif.ExifInterface;
@@ -1099,7 +1100,7 @@
}
@Override
- public void onMediaSaveServiceConnected(MediaSaveService s) {
+ public void onMediaSaverAvailable(MediaSaver s) {
// do nothing.
}
}
diff --git a/src/com/android/camera/app/MediaSaver.java b/src/com/android/camera/app/MediaSaver.java
new file mode 100644
index 0000000..ded7bf6
--- /dev/null
+++ b/src/com/android/camera/app/MediaSaver.java
@@ -0,0 +1,148 @@
+/*
+ * 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.app;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.location.Location;
+import android.net.Uri;
+
+import com.android.camera.exif.ExifInterface;
+
+/**
+ * An interface defining the media saver which saves media files in the
+ * background.
+ */
+public interface MediaSaver {
+
+ /**
+ * An interface defining the callback for task queue status changes.
+ */
+ public interface QueueListener {
+ /**
+ * The callback when the queue status changes. Every time a new
+ * {@link com.android.camera.app.MediaSaver.QueueListener} is set by
+ * {@link #setQueueListener(com.android.camera.app.MediaSaver.QueueListener)}
+ * this callback will be invoked to notify the current status of the
+ * queue.
+ *
+ * @param full Whether the queue is full.
+ */
+ public void onQueueStatus(boolean full);
+ }
+
+ /**
+ * An interface defining the callback when a media is saved.
+ */
+ public interface OnMediaSavedListener {
+ /**
+ * The callback when the saving is done in the background.
+ * @param uri The final content Uri of the saved media.
+ */
+ public void onMediaSaved(Uri uri);
+ }
+
+ /**
+ * Checks whether the queue is full.
+ */
+ boolean isQueueFull();
+
+ /**
+ * Adds an image into {@link android.content.ContentResolver} and also
+ * saves the file to the storage in the background.
+ *
+ * @param data The JPEG image data.
+ * @param title The title of the image.
+ * @param date The date when the image is created.
+ * @param loc The location where the image is created. Can be {@code null}.
+ * @param width The width of the image data before the orientation is
+ * applied.
+ * @param height The height of the image data before the orientation is
+ * applied.
+ * @param orientation The orientation of the image. The value should be a
+ * degree of rotation in clockwise. Valid values are
+ * 0, 90, 180 and 270.
+ * @param exif The EXIF data of this image.
+ * @param l A callback object used when the saving is done.
+ * @param resolver The {@link android.content.ContentResolver} to be
+ * updated.
+ */
+ void addImage(byte[] data, String title, long date, Location loc, int width, int height,
+ int orientation, ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver);
+
+ /**
+ * Adds an image into {@link android.content.ContentResolver} and also
+ * saves the file to the storage in the background. The width and height
+ * will be obtained directly from the image data.
+ *
+ * @param data The JPEG image data.
+ * @param title The title of the image.
+ * @param date The date when the image is created.
+ * @param loc The location where the image is created. Can be {@code null}.
+ * @param orientation The orientation of the image. The value should be a
+ * degree of rotation in clockwise. Valid values are
+ * 0, 90, 180 and 270.
+ * @param exif The EXIF data of this image.
+ * @param l A callback object used when the saving is done.
+ * @param resolver The {@link android.content.ContentResolver} to be
+ * updated.
+ */
+ void addImage(byte[] data, String title, long date, Location loc, int orientation,
+ ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver);
+
+ /**
+ * Adds an image into {@link android.content.ContentResolver} and also
+ * saves the file to the storage in the background. The time will be set by
+ * {@link System#currentTimeMillis()}.
+ * will be obtained directly from the image data.
+ *
+ * @param data The JPEG image data.
+ * @param title The title of the image.
+ * @param loc The location where the image is created. Can be {@code null}.
+ * @param width The width of the image data before the orientation is
+ * applied.
+ * @param height The height of the image data before the orientation is
+ * applied.
+ * @param orientation
+ * @param exif The EXIF data of this image.
+ * @param l A callback object used when the saving is done.
+ * @param resolver The {@link android.content.ContentResolver} to be
+ * updated.
+ */
+ void addImage(byte[] data, String title, Location loc, int width, int height, int orientation,
+ ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver);
+
+ /**
+ * Adds the video data into the {@link android.content.ContentResolver} in
+ * the background. Only the database is updated here. The file should
+ * already be created by {@link android.media.MediaRecorder}.
+ *
+ * @param path The path of the video file on the storage.
+ * @param duration The length of the video in millisecond.
+ * @param values The values to be stored in the database.
+ * @param l A callback object used when the saving is done.
+ * @param resolver The {@link android.content.ContentResolver} to be
+ * updated.
+ */
+ void addVideo(String path, long duration, ContentValues values, OnMediaSavedListener l,
+ ContentResolver resolver);
+
+ /**
+ * Sets the queue listener.
+ */
+ void setQueueListener(QueueListener l);
+}
diff --git a/src/com/android/camera/tinyplanet/TinyPlanetFragment.java b/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
index 9cde87b..f1a1082 100644
--- a/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
+++ b/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
@@ -42,8 +42,8 @@
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
import com.android.camera.CameraActivity;
-import com.android.camera.MediaSaveService;
-import com.android.camera.MediaSaveService.OnMediaSavedListener;
+import com.android.camera.app.MediaSaver.OnMediaSavedListener;
+import com.android.camera.app.MediaSaver;
import com.android.camera.exif.ExifInterface;
import com.android.camera.tinyplanet.TinyPlanetPreview.PreviewSizeListener;
import com.android.camera.util.XmpUtil;
@@ -53,7 +53,6 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.locks.Lock;
@@ -305,7 +304,7 @@
protected void onPostExecute(TinyPlanetImage image) {
// Once created, store the new file and add it to the filmstrip.
final CameraActivity activity = (CameraActivity) getActivity();
- MediaSaveService mediaSaveService = activity.getMediaSaveService();
+ MediaSaver mediaSaver = activity.getMediaSaver();
OnMediaSavedListener doneListener =
new OnMediaSavedListener() {
@Override
@@ -318,7 +317,7 @@
}
};
String tinyPlanetTitle = FILENAME_PREFIX + mOriginalTitle;
- mediaSaveService.addImage(image.mJpegData, tinyPlanetTitle, (new Date()).getTime(),
+ mediaSaver.addImage(image.mJpegData, tinyPlanetTitle, (new Date()).getTime(),
null,
image.mSize, image.mSize, 0, null, doneListener, getActivity()
.getContentResolver());