summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorztenghui <ztenghui@google.com>2013-06-10 15:51:42 -0700
committerztenghui <ztenghui@google.com>2013-06-19 11:20:26 -0700
commit41fd8171292a8a3248fe48eea362834ed5d25b90 (patch)
treecd89dad24a5a08ce48b62559ca39066d7f572923
parent5791e2fbe36756806339182fcde4ce058ec1d203 (diff)
downloadandroid_packages_apps_Snap-41fd8171292a8a3248fe48eea362834ed5d25b90.tar.gz
android_packages_apps_Snap-41fd8171292a8a3248fe48eea362834ed5d25b90.tar.bz2
android_packages_apps_Snap-41fd8171292a8a3248fe48eea362834ed5d25b90.zip
Setup the saving framework to support hiding original photo for editor.
This new approach will try to hide the original image into an auxiliary directory. User will only see one edited image, while being able to go back to the original image in the editor. Refactor SaveCopyTask for more private funcs and more comments. bug:9468909 Change-Id: I866321d23e6db0b3dbd08fec2a6a7e3142b17b65
-rw-r--r--src/com/android/gallery3d/data/LocalImage.java8
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java29
-rw-r--r--src/com/android/gallery3d/filtershow/cache/ImageLoader.java4
-rw-r--r--src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java288
-rw-r--r--src/com/android/gallery3d/util/SaveVideoFileUtils.java8
5 files changed, 274 insertions, 63 deletions
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index 1ed67ecf4..a7c98af77 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -35,9 +35,9 @@ import com.android.gallery3d.app.PanoramaMetadataSupport;
import com.android.gallery3d.app.StitchingProgressManager;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.exif.ExifTag;
+import com.android.gallery3d.filtershow.tools.SaveCopyTask;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.ThreadPool.Job;
import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -46,8 +46,6 @@ import com.android.gallery3d.util.UpdateHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel.MapMode;
// LocalImage represents an image in the local storage.
public class LocalImage extends LocalMediaItem {
@@ -271,7 +269,9 @@ public class LocalImage extends LocalMediaItem {
public void delete() {
GalleryUtils.assertNotInRenderThread();
Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
- mApplication.getContentResolver().delete(baseUri, "_id=?",
+ ContentResolver contentResolver = mApplication.getContentResolver();
+ SaveCopyTask.deleteAuxFiles(contentResolver, getContentUri());
+ contentResolver.delete(baseUri, "_id=?",
new String[]{String.valueOf(id)});
}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index fbe09b999..13aa3aa85 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -136,6 +136,8 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
private Uri mOriginalImageUri = null;
private ImagePreset mOriginalPreset = null;
+ private Uri mSelectedImageUri = null;
+
private CategoryAdapter mCategoryLooksAdapter = null;
private CategoryAdapter mCategoryBordersAdapter = null;
private CategoryAdapter mCategoryGeometryAdapter = null;
@@ -305,12 +307,13 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
}
mAction = intent.getAction();
- Uri srcUri = intent.getData();
+ mSelectedImageUri = intent.getData();
+ Uri loadUri = mSelectedImageUri;
if (mOriginalImageUri != null) {
- srcUri = mOriginalImageUri;
+ loadUri = mOriginalImageUri;
}
- if (srcUri != null) {
- startLoadBitmap(srcUri);
+ if (loadUri != null) {
+ startLoadBitmap(loadUri);
} else {
pickImage();
}
@@ -907,11 +910,13 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int id) {
saveImage();
}
});
builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int id) {
done();
}
@@ -965,7 +970,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
public void saveImage() {
if (mImageShow.hasModifications()) {
// Get the name of the album, to which the image will be saved
- File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
+ File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mSelectedImageUri);
int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
showSavingProgress(albumName);
@@ -981,10 +986,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
finish();
}
- static {
- System.loadLibrary("jni_filtershow_filters");
- }
-
private void extractXMPData() {
XMresults res = XmpPresets.extractXMPData(
getBaseContext(), mMasterImage, getIntent().getData());
@@ -994,4 +995,14 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
mOriginalImageUri = res.originalimage;
mOriginalPreset = res.preset;
}
+
+ public Uri getSelectedImageUri() {
+ return mSelectedImageUri;
+ }
+
+ static {
+ System.loadLibrary("jni_filtershow_filters");
+ }
+
+
}
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index 7ddd9bebe..491340e0d 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -394,7 +394,9 @@ public class ImageLoader {
public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
File destination) {
- new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() {
+ Uri selectedImageUri = filterShowActivity.getSelectedImageUri();
+ new SaveCopyTask(mContext, mUri, selectedImageUri, destination,
+ new SaveCopyTask.Callback() {
@Override
public void onComplete(Uri result) {
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
index 9f7cba30b..0d3d5ac06 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
@@ -25,6 +25,7 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
+import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;
@@ -39,6 +40,7 @@ import com.android.gallery3d.util.XmpUtilHelper;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Date;
@@ -60,7 +62,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
void onComplete(Uri result);
}
- private interface ContentResolverQueryCallback {
+ public interface ContentResolverQueryCallback {
void onCursorResult(Cursor cursor);
}
@@ -69,26 +71,68 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
private static final String PREFIX_PANO = "PANO";
private static final String PREFIX_IMG = "IMG";
private static final String POSTFIX_JPG = ".jpg";
+ private static final String AUX_DIR_NAME = ".aux";
+
+ private final Context mContext;
+ private final Uri mSourceUri;
+ private final Callback mCallback;
+ private final File mDestinationFile;
+ private final Uri mSelectedImageUri;
+
+ // In order to support the new edit-save behavior such that user won't see
+ // the edited image together with the original image, we are adding a new
+ // auxiliary directory for the edited image. Basically, the original image
+ // will be hidden in that directory after edit and user will see the edited
+ // image only.
+ // Note that deletion on the edited image will also cause the deletion of
+ // the original image under auxiliary directory.
+ //
+ // There are several situations we need to consider:
+ // 1. User edit local image local01.jpg. A local02.jpg will be created in the
+ // same directory, and original image will be moved to auxiliary directory as
+ // ./.aux/local02.jpg.
+ // If user edit the local02.jpg, local03.jpg will be created in the local
+ // directory and ./.aux/local02.jpg will be renamed to ./.aux/local03.jpg
+ //
+ // 2. User edit remote image remote01.jpg from picassa or other server.
+ // remoteSavedLocal01.jpg will be saved under proper local directory.
+ // In remoteSavedLocal01.jpg, there will be a reference pointing to the
+ // remote01.jpg. There will be no local copy of remote01.jpg.
+ // If user edit remoteSavedLocal01.jpg, then a new remoteSavedLocal02.jpg
+ // will be generated and still pointing to the remote01.jpg
+ //
+ // 3. User delete any local image local.jpg.
+ // Since the filenames are kept consistent in auxiliary directory, every
+ // time a local.jpg get deleted, the files in auxiliary directory whose
+ // names starting with "local." will be deleted.
+ // This pattern will facilitate the multiple images deletion in the auxiliary
+ // directory.
+ //
+ // TODO: Move the saving into a background service.
- private final Context context;
- private final Uri sourceUri;
- private final Callback callback;
- private final String saveFileName;
- private final File destinationFile;
-
- public SaveCopyTask(Context context, Uri sourceUri, File destination, Callback callback) {
- this.context = context;
- this.sourceUri = sourceUri;
- this.callback = callback;
-
+ /**
+ * @param context
+ * @param sourceUri The Uri for the original image, which can be the hidden
+ * image under the auxiliary directory or the same as selectedImageUri.
+ * @param selectedImageUri The Uri for the image selected by the user.
+ * In most cases, it is a content Uri for local image or remote image.
+ * @param destination Destinaton File, if this is null, a new file will be
+ * created under the same directory as selectedImageUri.
+ * @param callback Let the caller know the saving has completed.
+ * @return the newSourceUri
+ */
+ public SaveCopyTask(Context context, Uri sourceUri, Uri selectedImageUri,
+ File destination, Callback callback) {
+ mContext = context;
+ mSourceUri = sourceUri;
+ mCallback = callback;
if (destination == null) {
- this.destinationFile = getNewFile(context, sourceUri);
+ mDestinationFile = getNewFile(context, selectedImageUri);
} else {
- this.destinationFile = destination;
+ mDestinationFile = destination;
}
- saveFileName = PREFIX_IMG + new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(
- System.currentTimeMillis()));
+ mSelectedImageUri = selectedImageUri;
}
public static File getFinalSaveDirectory(Context context, Uri sourceUri) {
@@ -113,12 +157,64 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
return new File(saveDirectory, PREFIX_IMG + filename + POSTFIX_JPG);
}
+ /**
+ * Remove the files in the auxiliary directory whose names are the same as
+ * the source image.
+ * @param contentResolver The application's contentResolver
+ * @param srcContentUri The content Uri for the source image.
+ */
+ public static void deleteAuxFiles(ContentResolver contentResolver,
+ Uri srcContentUri) {
+ final String[] fullPath = new String[1];
+ String[] queryProjection = new String[] { ImageColumns.DATA };
+ querySourceFromContentResolver(contentResolver,
+ srcContentUri, queryProjection,
+ new ContentResolverQueryCallback() {
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ fullPath[0] = cursor.getString(0);
+ }
+ }
+ );
+ if (fullPath[0] != null) {
+ // Construct the auxiliary directory given the source file's path.
+ // Then select and delete all the files starting with the same name
+ // under the auxiliary directory.
+ File currentFile = new File(fullPath[0]);
+
+ String filename = currentFile.getName();
+ int firstDotPos = filename.indexOf(".");
+ final String filenameNoExt = (firstDotPos == -1) ? filename :
+ filename.substring(0, firstDotPos);
+ File auxDir = getLocalAuxDirectory(currentFile);
+ if (auxDir.exists()) {
+ FilenameFilter filter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ if (name.startsWith(filenameNoExt + ".")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+
+ // Delete all auxiliary files whose name is matching the
+ // current local image.
+ File[] auxFiles = auxDir.listFiles(filter);
+ for (File file : auxFiles) {
+ file.delete();
+ }
+ }
+ }
+ }
+
public Object getPanoramaXMPData(Uri source, ImagePreset preset) {
Object xmp = null;
if (preset.isPanoramaSafe()) {
InputStream is = null;
try {
- is = context.getContentResolver().openInputStream(source);
+ is = mContext.getContentResolver().openInputStream(source);
xmp = XmpUtilHelper.extractXMPMeta(is);
} catch (FileNotFoundException e) {
Log.w(LOGTAG, "Failed to get XMP data from image: ", e);
@@ -138,11 +234,11 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
public ExifInterface getExifData(Uri source) {
ExifInterface exif = new ExifInterface();
- String mimeType = context.getContentResolver().getType(sourceUri);
+ String mimeType = mContext.getContentResolver().getType(mSelectedImageUri);
if (mimeType.equals(ImageLoader.JPEG_MIME_TYPE)) {
InputStream inStream = null;
try {
- inStream = context.getContentResolver().openInputStream(source);
+ inStream = mContext.getContentResolver().openInputStream(source);
exif.readExif(inStream);
} catch (FileNotFoundException e) {
Log.w(LOGTAG, "Cannot find file: " + source, e);
@@ -174,7 +270,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
@Override
protected Uri doInBackground(ImagePreset... params) {
// TODO: Support larger dimensions for photo saving.
- if (params[0] == null || sourceUri == null) {
+ if (params[0] == null || mSourceUri == null || mSelectedImageUri == null) {
return null;
}
ImagePreset preset = params[0];
@@ -182,19 +278,25 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
Uri uri = null;
boolean noBitmap = true;
int num_tries = 0;
+
+ // If necessary, move the source file into the auxiliary directory,
+ // newSourceUri is then pointing to the new location.
+ // If no file is moved, newSourceUri will be the same as mSourceUri.
+ Uri newSourceUri = moveSrcToAuxIfNeeded(mSourceUri, mDestinationFile);
+
// Stopgap fix for low-memory devices.
while (noBitmap) {
try {
// Try to do bitmap operations, downsample if low-memory
- Bitmap bitmap = ImageLoader.loadMutableBitmap(context, sourceUri, options);
+ Bitmap bitmap = ImageLoader.loadMutableBitmap(mContext, newSourceUri, options);
if (bitmap == null) {
return null;
}
CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(), "Saving");
bitmap = pipeline.renderFinalImage(bitmap, preset);
- Object xmp = getPanoramaXMPData(sourceUri, preset);
- ExifInterface exif = getExifData(sourceUri);
+ Object xmp = getPanoramaXMPData(mSelectedImageUri, preset);
+ ExifInterface exif = getExifData(mSelectedImageUri);
// Set tags
long time = System.currentTimeMillis();
@@ -207,12 +309,23 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
exif.removeCompressedThumbnail();
// If we succeed in writing the bitmap as a jpeg, return a uri.
- if (putExifData(this.destinationFile, exif, bitmap)) {
- putPanoramaXMPData(this.destinationFile, xmp);
- uri = insertContent(context, sourceUri, this.destinationFile, saveFileName,
+ if (putExifData(mDestinationFile, exif, bitmap)) {
+ putPanoramaXMPData(mDestinationFile, xmp);
+ uri = insertContent(mContext, mSelectedImageUri, mDestinationFile,
time);
}
- XmpPresets.writeFilterXMP(context, sourceUri, this.destinationFile, preset);
+
+ // mDestinationFile will save the newSourceUri info in the XMP.
+ XmpPresets.writeFilterXMP(mContext, newSourceUri, mDestinationFile, preset);
+
+ // Since we have a new image inserted to media store, we can
+ // safely remove the old one which is selected by the user.
+ String scheme = mSelectedImageUri.getScheme();
+ if (scheme != null && scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ if (mSelectedImageUri.getAuthority().equals(MediaStore.AUTHORITY)) {
+ mContext.getContentResolver().delete(mSelectedImageUri, null, null);
+ }
+ }
noBitmap = false;
} catch (java.lang.OutOfMemoryError e) {
@@ -227,17 +340,73 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
return uri;
}
+ /**
+ * Move the source file to auxiliary directory if needed and return the Uri
+ * pointing to this new source file.
+ * @param srcUri Uri to the source image.
+ * @param dstFile Providing the destination file info to help to build the
+ * auxiliary directory and new source file's name.
+ * @return the newSourceUri pointing to the new source image.
+ */
+ private Uri moveSrcToAuxIfNeeded(Uri srcUri, File dstFile) {
+ File srcFile = getFileFromUri(mContext, srcUri);
+ if (srcFile == null) {
+ Log.d(LOGTAG, "Source file is not a local file, no update.");
+ return srcUri;
+ }
+
+ // Get the destination directory and create the auxilliary directory
+ // if necessary.
+ File auxDiretory = getLocalAuxDirectory(dstFile);
+ if (!auxDiretory.exists()) {
+ auxDiretory.mkdirs();
+ }
+
+ // Make sure there is a .nomedia file in the auxiliary directory, such
+ // that MediaScanner will not report those files under this directory.
+ File noMedia = new File(auxDiretory, ".nomedia");
+ if (!noMedia.exists()) {
+ try {
+ noMedia.createNewFile();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Can't create the nomedia");
+ return srcUri;
+ }
+ }
+ // We are using the destination file name such that photos sitting in
+ // the auxiliary directory are matching the parent directory.
+ File newSrcFile = new File(auxDiretory, dstFile.getName());
+
+ if (!newSrcFile.exists()) {
+ srcFile.renameTo(newSrcFile);
+ }
+
+ return Uri.fromFile(newSrcFile);
+
+ }
+
+ private static File getLocalAuxDirectory(File dstFile) {
+ File dstDirectory = dstFile.getParentFile();
+ File auxDiretory = new File(dstDirectory + "/" + AUX_DIR_NAME);
+ return auxDiretory;
+ }
@Override
protected void onPostExecute(Uri result) {
- if (callback != null) {
- callback.onComplete(result);
+ if (mCallback != null) {
+ mCallback.onComplete(result);
}
}
private static void querySource(Context context, Uri sourceUri, String[] projection,
ContentResolverQueryCallback callback) {
ContentResolver contentResolver = context.getContentResolver();
+ querySourceFromContentResolver(contentResolver, sourceUri, projection, callback);
+ }
+
+ private static void querySourceFromContentResolver(
+ ContentResolver contentResolver, Uri sourceUri, String[] projection,
+ ContentResolverQueryCallback callback) {
Cursor cursor = null;
try {
cursor = contentResolver.query(sourceUri, projection, null, null,
@@ -255,18 +424,51 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
}
private static File getSaveDirectory(Context context, Uri sourceUri) {
- final File[] dir = new File[1];
- querySource(context, sourceUri, new String[] {
- ImageColumns.DATA
- },
- new ContentResolverQueryCallback() {
+ File file = getFileFromUri(context, sourceUri);
+ if (file != null) {
+ return file.getParentFile();
+ } else {
+ return null;
+ }
+ }
- @Override
- public void onCursorResult(Cursor cursor) {
- dir[0] = new File(cursor.getString(0)).getParentFile();
- }
- });
- return dir[0];
+ /**
+ * Construct a File object based on the srcUri.
+ * @return The file object. Return null if srcUri is invalid or not a local
+ * file.
+ */
+ private static File getFileFromUri(Context context, Uri srcUri) {
+ if (srcUri == null) {
+ Log.e(LOGTAG, "srcUri is null.");
+ return null;
+ }
+
+ String scheme = srcUri.getScheme();
+ if (scheme == null) {
+ Log.e(LOGTAG, "scheme is null.");
+ return null;
+ }
+
+ final File[] file = new File[1];
+ // sourceUri can be a file path or a content Uri, it need to be handled
+ // differently.
+ if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ if (srcUri.getAuthority().equals(MediaStore.AUTHORITY)) {
+ querySource(context, srcUri, new String[] {
+ ImageColumns.DATA
+ },
+ new ContentResolverQueryCallback() {
+
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ file[0] = new File(cursor.getString(0));
+ }
+ });
+ }
+ } else if (scheme.equals(ContentResolver.SCHEME_FILE)) {
+ file[0] = new File(srcUri.getPath());
+ }
+ return file[0];
}
/**
@@ -299,16 +501,16 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
/**
* Insert the content (saved file) with proper source photo properties.
*/
- public static Uri insertContent(Context context, Uri sourceUri, File file, String saveFileName,
+ private static Uri insertContent(Context context, Uri sourceUri, File file,
long time) {
time /= 1000;
final ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, saveFileName);
+ values.put(Images.Media.TITLE, file.getName());
values.put(Images.Media.DISPLAY_NAME, file.getName());
values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put(Images.Media.DATE_TAKEN, time);
- values.put(Images.Media.DATE_MODIFIED, time);
+ values.put(Images.Media.DATE_MODIFIED, System.currentTimeMillis());
values.put(Images.Media.DATE_ADDED, time);
values.put(Images.Media.ORIENTATION, 0);
values.put(Images.Media.DATA, file.getAbsolutePath());
diff --git a/src/com/android/gallery3d/util/SaveVideoFileUtils.java b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
index e2c5f51b9..da0970b1d 100644
--- a/src/com/android/gallery3d/util/SaveVideoFileUtils.java
+++ b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
@@ -25,17 +25,13 @@ import android.os.Environment;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video.VideoColumns;
+import com.android.gallery3d.filtershow.tools.SaveCopyTask.ContentResolverQueryCallback;
+
import java.io.File;
import java.sql.Date;
import java.text.SimpleDateFormat;
public class SaveVideoFileUtils {
- // Copy from SaveCopyTask.java in terms of how to handle the destination
- // path and filename : querySource() and getSaveDirectory().
- public interface ContentResolverQueryCallback {
- void onCursorResult(Cursor cursor);
- }
-
// This function can decide which folder to save the video file, and generate
// the needed information for the video file including filename.
public static SaveVideoFileInfo getDstMp4FileInfo(String fileNameFormat,