diff options
author | nicolasroard <nicolasroard@google.com> | 2012-10-01 00:25:11 -0700 |
---|---|---|
committer | nicolasroard <nicolasroard@google.com> | 2012-10-02 17:27:00 -0700 |
commit | e091f88847d6c1a142f3503a1d0d42f5ad0ba188 (patch) | |
tree | 463ac342c9cef88d60c1a1667c41a622a7c06d5f | |
parent | 1ebac746eb319663f44fe9fbb7786cb9bb87489b (diff) | |
download | android_packages_apps_Snap-e091f88847d6c1a142f3503a1d0d42f5ad0ba188.tar.gz android_packages_apps_Snap-e091f88847d6c1a142f3503a1d0d42f5ad0ba188.tar.bz2 android_packages_apps_Snap-e091f88847d6c1a142f3503a1d0d42f5ad0ba188.zip |
Implements image sharing
bug:7233986
Change-Id: I8feb94d77facf8dbb8da5fab89b49ed7c224116e
6 files changed, 255 insertions, 68 deletions
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 4570eb017..b50ab94d6 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -1,6 +1,8 @@ package com.android.gallery3d.filtershow; +import java.io.File; +import java.io.IOException; import java.util.Vector; import com.android.gallery3d.filtershow.cache.ImageLoader; @@ -10,6 +12,8 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter; import com.android.gallery3d.filtershow.imageshow.ImageStraighten; import com.android.gallery3d.filtershow.presets.*; +import com.android.gallery3d.filtershow.provider.SharedImageProvider; +import com.android.gallery3d.filtershow.tools.SaveCopyTask; import com.android.gallery3d.filtershow.ui.ImageCurves; import com.android.gallery3d.R; @@ -18,6 +22,7 @@ import android.os.Bundle; import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; +import android.content.ContentValues; import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; @@ -31,7 +36,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; -import android.widget.AbsoluteLayout; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; @@ -40,10 +44,13 @@ import android.widget.FrameLayout.LayoutParams; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.ShareActionProvider; +import android.widget.ShareActionProvider.OnShareTargetSelectedListener; import android.widget.Toast; @TargetApi(16) -public class FilterShowActivity extends Activity implements OnItemClickListener { +public class FilterShowActivity extends Activity implements OnItemClickListener, + OnShareTargetSelectedListener { private ImageLoader mImageLoader = null; private ImageShow mImageShow = null; @@ -84,6 +91,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener private Vector<ImageButton> mBottomPanelButtons = new Vector<ImageButton>(); private Vector<ImageButton> mColorsPanelButtons = new Vector<ImageButton>(); + private ShareActionProvider mShareActionProvider; + private File mSharedOutputFile = null; + + private boolean mSharingImage = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -210,6 +222,46 @@ public class FilterShowActivity extends Activity implements OnItemClickListener } } + public void completeSaveImage(Uri saveUri) { + if (mSharingImage && mSharedOutputFile != null) { + // Image saved, we unblock the content provider + Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, + Uri.encode(mSharedOutputFile.getAbsolutePath())); + ContentValues values = new ContentValues(); + values.put(SharedImageProvider.PREPARE, false); + getContentResolver().insert(uri, values); + } + setResult(RESULT_OK, new Intent().setData(saveUri)); + finish(); + } + + @Override + public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) { + // First, let's tell the SharedImageProvider that it will need to wait for the image + Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, + Uri.encode(mSharedOutputFile.getAbsolutePath())); + ContentValues values = new ContentValues(); + values.put(SharedImageProvider.PREPARE, true); + getContentResolver().insert(uri, values); + mSharingImage = true; + + // Process and save the image in the background. + mImageShow.saveImage(this, mSharedOutputFile); + return true; + } + + private Intent getDefaultShareIntent() { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType(SharedImageProvider.MIME_TYPE); + mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri()); + Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, + Uri.encode(mSharedOutputFile.getAbsolutePath())); + intent.putExtra(Intent.EXTRA_STREAM, uri); + return intent; + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu); @@ -225,6 +277,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener } else { showState.setTitle(R.string.show_imagestate_panel); } + mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share) + .getActionProvider(); + mShareActionProvider.setShareIntent(getDefaultShareIntent()); + mShareActionProvider.setOnShareTargetSelectedListener(this); return true; } @@ -865,12 +921,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); - mImageShow.saveImage(this); - } - - public void completeSaveImage(Uri saveUri) { - setResult(RESULT_OK, new Intent().setData(saveUri)); - finish(); + mImageShow.saveImage(this, null); } static { diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 2c8fff9a2..100a17bf0 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -2,6 +2,7 @@ package com.android.gallery3d.filtershow.cache; import java.io.Closeable; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -13,6 +14,7 @@ import com.android.gallery3d.filtershow.HistoryAdapter; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.presets.ImagePreset; import com.android.gallery3d.filtershow.tools.SaveCopyTask; +import com.android.gallery3d.filtershow.tools.ProcessedBitmap; import com.android.gallery3d.R; import android.content.Context; @@ -59,6 +61,10 @@ public class ImageLoader { updateBitmaps(); } + public Uri getUri() { + return mUri; + } + private int getOrientation(Uri uri) { Cursor cursor = null; try { @@ -219,7 +225,8 @@ public class ImageLoader { mCache.reset(imagePreset); } - public Uri saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity) { + public Uri saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity, + File destination) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inMutable = true; @@ -235,15 +242,15 @@ public class ImageLoader { // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't // exist) mSaveCopy = mFullOriginalBitmap; - preset.apply(mSaveCopy); - new SaveCopyTask(mContext, mUri, new SaveCopyTask.Callback() { + ProcessedBitmap processedBitmap = new ProcessedBitmap(mSaveCopy, preset); + new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() { @Override public void onComplete(Uri result) { filterShowActivity.completeSaveImage(result); } - }).execute(mSaveCopy); + }).execute(processedBitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 14bc0be20..2caa2d578 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -13,6 +13,8 @@ import com.android.gallery3d.R; import com.android.gallery3d.R.id; import com.android.gallery3d.R.layout; +import java.io.File; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -288,8 +290,8 @@ public class ImageShow extends View implements SliderListener { mFilteredImage = bitmap; } - public void saveImage(FilterShowActivity filterShowActivity) { - mImageLoader.saveImage(getImagePreset(), filterShowActivity); + public void saveImage(FilterShowActivity filterShowActivity, File file) { + mImageLoader.saveImage(getImagePreset(), filterShowActivity, file); } public boolean onTouchEvent(MotionEvent event) { diff --git a/src/com/android/gallery3d/filtershow/provider/SharedImageProvider.java b/src/com/android/gallery3d/filtershow/provider/SharedImageProvider.java new file mode 100644 index 000000000..dff15164b --- /dev/null +++ b/src/com/android/gallery3d/filtershow/provider/SharedImageProvider.java @@ -0,0 +1,123 @@ + +package com.android.gallery3d.filtershow.provider; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.ConditionVariable; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; + +public class SharedImageProvider extends ContentProvider { + + private static final String LOGTAG = "SharedImageProvider"; + + public static final String MIME_TYPE = "image/jpeg"; + public static final String AUTHORITY = "com.android.gallery3d.filtershow.provider.SharedImageProvider"; + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/image"); + public static final String PREPARE = "prepare"; + + private final String[] mMimeStreamType = { + MIME_TYPE + }; + + private static ConditionVariable mImageReadyCond = new ConditionVariable(false); + + @Override + public int delete(Uri arg0, String arg1, String[] arg2) { + return 0; + } + + @Override + public String getType(Uri arg0) { + return MIME_TYPE; + } + + @Override + public String[] getStreamTypes(Uri arg0, String mimeTypeFilter) { + return mMimeStreamType; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + if (values.containsKey(PREPARE)) { + if (values.getAsBoolean(PREPARE)) { + mImageReadyCond.close(); + } else { + mImageReadyCond.open(); + } + } + return null; + } + + @Override + public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { + return 0; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + String uriPath = uri.getLastPathSegment(); + if (uriPath == null) { + return null; + } + if (projection == null) { + projection = new String[] { + BaseColumns._ID, + MediaStore.MediaColumns.DATA, + OpenableColumns.DISPLAY_NAME, + OpenableColumns.SIZE + }; + } + // If we receive a query on display name or size, + // we should block until the image is ready + mImageReadyCond.block(); + + File path = new File(uriPath); + + MatrixCursor cursor = new MatrixCursor(projection); + Object[] columns = new Object[projection.length]; + for (int i = 0; i < projection.length; i++) { + if (projection[i].equalsIgnoreCase(BaseColumns._ID)) { + columns[i] = 0; + } else if (projection[i].equalsIgnoreCase(MediaStore.MediaColumns.DATA)) { + columns[i] = uri; + } else if (projection[i].equalsIgnoreCase(OpenableColumns.DISPLAY_NAME)) { + columns[i] = path.getName(); + } else if (projection[i].equalsIgnoreCase(OpenableColumns.SIZE)) { + columns[i] = path.length(); + } + } + cursor.addRow(columns); + + return cursor; + } + + public ParcelFileDescriptor openFile(Uri uri, String mode) + throws FileNotFoundException { + String uriPath = uri.getLastPathSegment(); + if (uriPath == null) { + return null; + } + // Here we need to block until the image is ready + mImageReadyCond.block(); + File path = new File(uriPath); + int imode = 0; + imode |= ParcelFileDescriptor.MODE_READ_ONLY; + return ParcelFileDescriptor.open(path, imode); + } +} diff --git a/src/com/android/gallery3d/filtershow/tools/ProcessedBitmap.java b/src/com/android/gallery3d/filtershow/tools/ProcessedBitmap.java new file mode 100644 index 000000000..2e23b92eb --- /dev/null +++ b/src/com/android/gallery3d/filtershow/tools/ProcessedBitmap.java @@ -0,0 +1,18 @@ +package com.android.gallery3d.filtershow.tools; + +import android.graphics.Bitmap; + +import com.android.gallery3d.filtershow.presets.ImagePreset; + +public class ProcessedBitmap { + private Bitmap mBitmap; + private ImagePreset mPreset; + public ProcessedBitmap(Bitmap bitmap, ImagePreset preset) { + mBitmap = bitmap; + mPreset = preset; + } + public Bitmap apply() { + mPreset.apply(mBitmap); + return mBitmap; + } +}
\ No newline at end of file diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java index 8ca21e285..49cc33a2a 100644 --- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java +++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java @@ -30,6 +30,8 @@ import android.provider.MediaStore.Images.ImageColumns; import android.view.Gravity; import android.widget.Toast; +import com.android.gallery3d.filtershow.presets.ImagePreset; + //import com.android.gallery3d.R; //import com.android.gallery3d.util.BucketNames; @@ -45,46 +47,28 @@ import java.text.SimpleDateFormat; /** * Asynchronous task for saving edited photo as a new copy. */ -public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { +public class SaveCopyTask extends AsyncTask<ProcessedBitmap, Void, Uri> { public static final String DOWNLOAD = "download"; public static final String DEFAULT_SAVE_DIRECTORY = "Download"; private static final int DEFAULT_COMPRESS_QUALITY = 95; /** - * Saves the bitmap by given directory, filename, and format; if the - * directory is given null, then saves it under the cache directory. + * Saves the bitmap in the final destination */ - public File saveBitmap(Bitmap bitmap, File directory, String filename, - CompressFormat format) { - - if (directory == null) { - directory = context.getCacheDir(); - } else { - // Check if the given directory exists or try to create it. - if (!directory.isDirectory() && !directory.mkdirs()) { - return null; - } - } - - File file = null; + public static void saveBitmap(Bitmap bitmap, File destination) { OutputStream os = null; - try { - filename = (format == CompressFormat.PNG) ? filename + ".png" - : filename + ".jpg"; - file = new File(directory, filename); - os = new FileOutputStream(file); - bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os); + os = new FileOutputStream(destination); + bitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, os); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { closeStream(os); } - return file; } - private void closeStream(Closeable stream) { + private static void closeStream(Closeable stream) { if (stream != null) { try { stream.close(); @@ -113,62 +97,63 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { private final Uri sourceUri; private final Callback callback; private final String saveFileName; - private String saveFolderName; + private final File destinationFile; - public SaveCopyTask(Context context, Uri sourceUri, Callback callback) { + public SaveCopyTask(Context context, Uri sourceUri, File destination, Callback callback) { this.context = context; this.sourceUri = sourceUri; this.callback = callback; + if (destination == null) { + this.destinationFile = getNewFile(context, sourceUri); + } else { + this.destinationFile = destination; + } + saveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( System.currentTimeMillis())); } + public static File getNewFile(Context context, Uri sourceUri) { + File saveDirectory = getSaveDirectory(context, sourceUri); + if ((saveDirectory == null) || !saveDirectory.canWrite()) { + saveDirectory = new File(Environment.getExternalStorageDirectory(), + DOWNLOAD); + } + + String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( + System.currentTimeMillis())); + return new File(saveDirectory, filename + ".JPG"); + } + /** * The task should be executed with one given bitmap to be saved. */ @Override - protected Uri doInBackground(Bitmap... params) { + protected Uri doInBackground(ProcessedBitmap... params) { // TODO: Support larger dimensions for photo saving. if (params[0] == null) { return null; } - // Use the default save directory if the source directory cannot be - // saved. - File saveDirectory = getSaveDirectory(); - if ((saveDirectory == null) || !saveDirectory.canWrite()) { - saveDirectory = new File(Environment.getExternalStorageDirectory(), - DOWNLOAD); - saveFolderName = DEFAULT_SAVE_DIRECTORY; - } else { - saveFolderName = saveDirectory.getName(); - } - Bitmap bitmap = params[0]; + ProcessedBitmap processedBitmap = params[0]; - File file = saveBitmap(bitmap, saveDirectory, saveFileName, - Bitmap.CompressFormat.JPEG); + Bitmap bitmap = processedBitmap.apply(); + saveBitmap(bitmap, this.destinationFile); - Uri uri = (file != null) ? insertContent(file) : null; + Uri uri = insertContent(context, sourceUri, this.destinationFile, saveFileName); bitmap.recycle(); return uri; } @Override protected void onPostExecute(Uri result) { - /* - * String message = (result == null) ? - * context.getString(R.string.saving_failure) : - * context.getString(R.string.photo_saved, saveFolderName); Toast toast - * = Toast.makeText(context, message, Toast.LENGTH_SHORT); - * toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); - */ if (callback != null) { callback.onComplete(result); } } - private void querySource(String[] projection, + private static void querySource(Context context, Uri sourceUri, String[] projection, ContentResolverQueryCallback callback) { ContentResolver contentResolver = context.getContentResolver(); Cursor cursor = null; @@ -187,10 +172,10 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { } } - private File getSaveDirectory() { + private static File getSaveDirectory(Context context, Uri sourceUri) { final File[] dir = new File[1]; - querySource(new String[] { - ImageColumns.DATA + querySource(context, sourceUri, new String[] { + ImageColumns.DATA }, new ContentResolverQueryCallback() { @@ -205,7 +190,7 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { /** * Insert the content (saved file) with proper source photo properties. */ - private Uri insertContent(File file) { + public static Uri insertContent(Context context, Uri sourceUri, File file, String saveFileName) { long now = System.currentTimeMillis() / 1000; final ContentValues values = new ContentValues(); @@ -223,7 +208,7 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { ImageColumns.DATE_TAKEN, ImageColumns.LATITUDE, ImageColumns.LONGITUDE, }; - querySource(projection, new ContentResolverQueryCallback() { + querySource(context, sourceUri, projection, new ContentResolverQueryCallback() { @Override public void onCursorResult(Cursor cursor) { @@ -243,4 +228,5 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { return context.getContentResolver().insert( Images.Media.EXTERNAL_CONTENT_URI, values); } + } |