Add background processing service
bug:7298624
Change-Id: Ie79f88fd84fdf8f4dab6a8071f06a819e247b357
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 68780f7..ef1d914 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -219,6 +219,11 @@
android:grantUriPermissions="true"
android:readPermission="com.android.gallery3d.filtershow.permission.READ"
android:writePermission="com.android.gallery3d.filtershow.permission.WRITE" />
+
+ <service
+ android:name=".filtershow.pipeline.ProcessingService"
+ android:exported="false" />
+
<activity
android:name="com.android.gallery3d.filtershow.FilterShowActivity"
android:label="@string/title_activity_filter_show"
diff --git a/res/values/filtershow_strings.xml b/res/values/filtershow_strings.xml
index e142c0a..0a33108 100644
--- a/res/values/filtershow_strings.xml
+++ b/res/values/filtershow_strings.xml
@@ -199,4 +199,9 @@
<!-- Name used to indicate the final image in the state panel [CHAR LIMIT=20] -->
<string name="state_panel_result">Result</string>
+ <!-- Label for the notification [CHAR LIMIT=50] -->
+ <string name="filtershow_notification_label">Saving Image</string>
+ <!-- Label for the notification message [CHAR LIMIT=50] -->
+ <string name="filtershow_notification_message">Processing...</string>
+
</resources>
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index a7c98af..eb1efae 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -37,7 +37,7 @@
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.exif.ExifTag;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.tools.SaveImage;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.ThreadPool.Job;
import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -270,7 +270,7 @@
GalleryUtils.assertNotInRenderThread();
Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = mApplication.getContentResolver();
- SaveCopyTask.deleteAuxFiles(contentResolver, getContentUri());
+ SaveImage.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 bee764b..41d1784 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -19,28 +19,29 @@
import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.ProgressDialog;
+import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
-import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -55,20 +56,19 @@
import android.widget.Toast;
import com.android.gallery3d.R;
+import com.android.gallery3d.app.PhotoPage;
import com.android.gallery3d.data.LocalAlbum;
import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
import com.android.gallery3d.filtershow.pipeline.FilteringPipeline;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.category.Action;
import com.android.gallery3d.filtershow.category.CategoryAdapter;
-import com.android.gallery3d.filtershow.category.CategoryView;
import com.android.gallery3d.filtershow.category.MainPanel;
import com.android.gallery3d.filtershow.editors.BasicEditor;
import com.android.gallery3d.filtershow.editors.Editor;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.editors.EditorDraw;
import com.android.gallery3d.filtershow.editors.EditorFlip;
-import com.android.gallery3d.filtershow.editors.EditorInfo;
import com.android.gallery3d.filtershow.editors.EditorManager;
import com.android.gallery3d.filtershow.editors.EditorPanel;
import com.android.gallery3d.filtershow.editors.EditorRedEye;
@@ -76,8 +76,6 @@
import com.android.gallery3d.filtershow.editors.EditorStraighten;
import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
-import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
-import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.filters.ImageFilter;
@@ -88,9 +86,10 @@
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
import com.android.gallery3d.filtershow.pipeline.ImagePreset;
+import com.android.gallery3d.filtershow.pipeline.ProcessingService;
import com.android.gallery3d.filtershow.provider.SharedImageProvider;
import com.android.gallery3d.filtershow.state.StateAdapter;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.tools.SaveImage;
import com.android.gallery3d.filtershow.tools.XmpPresets;
import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
import com.android.gallery3d.filtershow.ui.FramedTextButton;
@@ -101,6 +100,7 @@
import java.io.File;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Vector;
public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
@@ -149,6 +149,75 @@
private CategoryAdapter mCategoryFiltersAdapter = null;
private int mCurrentPanel = MainPanel.LOOKS;
+ private ProcessingService mBoundService;
+ private boolean mIsBound = false;
+
+ public ProcessingService getProcessingService() {
+ return mBoundService;
+ }
+
+ public boolean isSimpleEditAction() {
+ return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ /*
+ * This is called when the connection with the service has been
+ * established, giving us the service object we can use to
+ * interact with the service. Because we have bound to a explicit
+ * service that we know is running in our own process, we can
+ * cast its IBinder to a concrete class and directly access it.
+ */
+ mBoundService = ((ProcessingService.LocalBinder)service).getService();
+ mBoundService.setFiltershowActivity(FilterShowActivity.this);
+ mBoundService.onStart();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ /*
+ * This is called when the connection with the service has been
+ * unexpectedly disconnected -- that is, its process crashed.
+ * Because it is running in our same process, we should never
+ * see this happen.
+ */
+ mBoundService = null;
+ }
+ };
+
+ void doBindService() {
+ /*
+ * Establish a connection with the service. We use an explicit
+ * class name because we want a specific service implementation that
+ * we know will be running in our own process (and thus won't be
+ * supporting component replacement by other applications).
+ */
+ bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ mIsBound = true;
+ }
+
+ void doUnbindService() {
+ if (mIsBound) {
+ // Detach our existing connection.
+ unbindService(mConnection);
+ mIsBound = false;
+ }
+ }
+
+ private void setupPipeline() {
+ doBindService();
+ ImageFilter.setActivityForMemoryToasts(this);
+ }
+
+ public void updateUIAfterServiceStarted() {
+ fillCategories();
+ loadMainPanel();
+ setDefaultPreset();
+ extractXMPData();
+ processIntent();
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -160,19 +229,13 @@
MasterImage.setMaster(mMasterImage);
clearGalleryBitmapPool();
+ setupPipeline();
- CachingPipeline.createRenderscriptContext(this);
setupMasterImage();
setDefaultValues();
fillEditors();
loadXML();
- loadMainPanel();
-
- setDefaultPreset();
-
- extractXMPData();
- processIntent();
UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
@@ -251,26 +314,25 @@
setupEditors();
mEditorPlaceHolder.hide();
-
mImageShow.bindAsImageLoadListener();
- fillFx();
- fillBorders();
- fillGeometry();
- fillFilters();
-
setupStatePanel();
}
+ public void fillCategories() {
+ fillLooks();
+ fillBorders();
+ fillTools();
+ fillEffects();
+ }
+
public void setupStatePanel() {
MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
}
- private void fillFilters() {
- Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
+ private void fillEffects() {
FiltersManager filtersManager = FiltersManager.getManager();
- filtersManager.addEffects(filtersRepresentations);
-
+ ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
mCategoryFiltersAdapter = new CategoryAdapter(this);
for (FilterRepresentation representation : filtersRepresentations) {
if (representation.getTextId() != 0) {
@@ -280,28 +342,9 @@
}
}
- private void fillGeometry() {
- Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
+ private void fillTools() {
FiltersManager filtersManager = FiltersManager.getManager();
-
- GeometryMetadata geo = new GeometryMetadata();
- int[] editorsId = geo.getEditorIds();
- for (int i = 0; i < editorsId.length; i++) {
- int editorId = editorsId[i];
- GeometryMetadata geometry = new GeometryMetadata(geo);
- geometry.setEditorId(editorId);
- EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId);
- geometry.setTextId(editorInfo.getTextId());
- geometry.setOverlayId(editorInfo.getOverlayId());
- geometry.setOverlayOnly(editorInfo.getOverlayOnly());
- if (geometry.getTextId() != 0) {
- geometry.setName(getString(geometry.getTextId()));
- }
- filtersRepresentations.add(geometry);
- }
-
- filtersManager.addTools(filtersRepresentations);
-
+ ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
mCategoryGeometryAdapter = new CategoryAdapter(this);
for (FilterRepresentation representation : filtersRepresentations) {
mCategoryGeometryAdapter.add(new Action(this, representation));
@@ -331,7 +374,6 @@
mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
EditorManager.addEditors(mEditorPlaceHolder);
mEditorPlaceHolder.setOldViews(mImageViews);
-
}
private void fillEditors() {
@@ -347,10 +389,7 @@
}
private void setDefaultValues() {
- ImageFilter.setActivityForMemoryToasts(this);
-
Resources res = getResources();
- FiltersManager.setResources(res);
// TODO: get those values from XML.
FramedTextButton.setTextSize((int) getPixelsFromDip(14));
@@ -379,16 +418,11 @@
}
private void fillBorders() {
- Vector<FilterRepresentation> borders = new Vector<FilterRepresentation>();
-
- // The "no border" implementation
- borders.add(new FilterImageBorderRepresentation(0));
-
- // Google-build borders
- FiltersManager.getManager().addBorders(this, borders);
+ FiltersManager filtersManager = FiltersManager.getManager();
+ ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
for (int i = 0; i < borders.size(); i++) {
- FilterRepresentation filter = borders.elementAt(i);
+ FilterRepresentation filter = borders.get(i);
filter.setName(getString(R.string.borders));
if (i == 0) {
filter.setName(getString(R.string.none));
@@ -628,16 +662,7 @@
if (mLoadBitmapTask != null) {
mLoadBitmapTask.cancel(false);
}
- // TODO: refactor, don't use so many singletons.
- FilteringPipeline.getPipeline().turnOnPipeline(false);
- MasterImage.reset();
- FilteringPipeline.reset();
- ImageFilter.resetStatics();
- FiltersManager.getPreviewManager().freeRSFilterScripts();
- FiltersManager.getManager().freeRSFilterScripts();
- FiltersManager.getHighresManager().freeRSFilterScripts();
- FiltersManager.reset();
- CachingPipeline.destroyRenderScriptContext();
+ doUnbindService();
super.onDestroy();
}
@@ -713,7 +738,7 @@
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, MasterImage.getImage().getUri());
+ mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
Uri.encode(mSharedOutputFile.getAbsolutePath()));
intent.putExtra(Intent.EXTRA_STREAM, uri);
@@ -744,7 +769,6 @@
@Override
public void onPause() {
super.onPause();
- rsPause();
if (mShareActionProvider != null) {
mShareActionProvider.setOnShareTargetSelectedListener(null);
}
@@ -753,48 +777,11 @@
@Override
public void onResume() {
super.onResume();
- rsResume();
if (mShareActionProvider != null) {
mShareActionProvider.setOnShareTargetSelectedListener(this);
}
}
- private void rsResume() {
- ImageFilter.setActivityForMemoryToasts(this);
- MasterImage.setMaster(mMasterImage);
- if (CachingPipeline.getRenderScriptContext() == null) {
- CachingPipeline.createRenderscriptContext(this);
- }
- FiltersManager.setResources(getResources());
- if (!mLoading) {
- Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
- FilteringPipeline pipeline = FilteringPipeline.getPipeline();
- pipeline.setOriginal(largeBitmap);
- float previewScale = (float) largeBitmap.getWidth() /
- (float) MasterImage.getImage().getOriginalBounds().width();
- pipeline.setPreviewScaleFactor(previewScale);
- Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
- if (highresBitmap != null) {
- float highResPreviewScale = (float) highresBitmap.getWidth() /
- (float) MasterImage.getImage().getOriginalBounds().width();
- pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
- }
- pipeline.turnOnPipeline(true);
- MasterImage.getImage().setOriginalGeometry(largeBitmap);
- }
- }
-
- private void rsPause() {
- FilteringPipeline.getPipeline().turnOnPipeline(false);
- FilteringPipeline.reset();
- ImageFilter.resetStatics();
- FiltersManager.getPreviewManager().freeRSFilterScripts();
- FiltersManager.getManager().freeRSFilterScripts();
- FiltersManager.getHighresManager().freeRSFilterScripts();
- FiltersManager.reset();
- CachingPipeline.destroyRenderScriptContext();
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -844,16 +831,13 @@
}
}
- private void fillFx() {
- FilterFxRepresentation nullFx =
- new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
- Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
- FiltersManager.getManager().addLooks(this, filtersRepresentations);
+ private void fillLooks() {
+ FiltersManager filtersManager = FiltersManager.getManager();
+ ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
mCategoryLooksAdapter = new CategoryAdapter(this);
int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
- mCategoryLooksAdapter.add(new Action(this, nullFx, Action.FULL_VIEW));
for (FilterRepresentation representation : filtersRepresentations) {
mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
}
@@ -1030,7 +1014,7 @@
public void saveImage() {
if (mImageShow.hasModifications()) {
// Get the name of the album, to which the image will be saved
- File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mSelectedImageUri);
+ File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
showSavingProgress(albumName);
@@ -1063,9 +1047,4 @@
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 7c594c6..b6c72fd 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -18,6 +18,7 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -33,6 +34,7 @@
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
+import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
@@ -286,17 +288,20 @@
* @param uri URI of image to open.
* @param context context whose ContentResolver to use.
* @param maxSideLength max side length of returned bitmap.
- * @param originalBounds set to the actual bounds of the stored bitmap.
+ * @param originalBounds If not null, set to the actual bounds of the stored bitmap.
+ * @param useMin use min or max side of the original image
* @return downsampled bitmap or null if this operation failed.
*/
public static Bitmap loadConstrainedBitmap(Uri uri, Context context, int maxSideLength,
- Rect originalBounds) {
- if (maxSideLength <= 0 || originalBounds == null || uri == null || context == null) {
+ Rect originalBounds, boolean useMin) {
+ if (maxSideLength <= 0 || uri == null || context == null) {
throw new IllegalArgumentException("bad argument to getScaledBitmap");
}
// Get width and height of stored bitmap
Rect storedBounds = loadBitmapBounds(context, uri);
- originalBounds.set(storedBounds);
+ if (originalBounds != null) {
+ originalBounds.set(storedBounds);
+ }
int w = storedBounds.width();
int h = storedBounds.height();
@@ -306,7 +311,12 @@
}
// Find best downsampling size
- int imageSide = Math.max(w, h);
+ int imageSide = 0;
+ if (useMin) {
+ imageSide = Math.min(w, h);
+ } else {
+ imageSide = Math.max(w, h);
+ }
int sampleSize = 1;
while (imageSide > maxSideLength) {
imageSide >>>= 1;
@@ -336,7 +346,7 @@
*/
public static Bitmap loadOrientedConstrainedBitmap(Uri uri, Context context, int maxSideLength,
int orientation, Rect originalBounds) {
- Bitmap bmap = loadConstrainedBitmap(uri, context, maxSideLength, originalBounds);
+ Bitmap bmap = loadConstrainedBitmap(uri, context, maxSideLength, originalBounds, false);
if (bmap != null) {
bmap = orientBitmap(bmap, orientation);
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropActivity.java b/src/com/android/gallery3d/filtershow/crop/CropActivity.java
index d14c090..0a0c367 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropActivity.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropActivity.java
@@ -45,7 +45,7 @@
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.filtershow.cache.ImageLoader;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.tools.SaveImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -264,7 +264,7 @@
protected Bitmap doInBackground(Uri... params) {
Uri uri = params[0];
Bitmap bmap = ImageLoader.loadConstrainedBitmap(uri, mContext, mBitmapSize,
- mOriginalBounds);
+ mOriginalBounds, false);
mOrientation = ImageLoader.getMetadataRotation(mContext, uri);
return bmap;
}
@@ -299,7 +299,7 @@
}
}
if (flags == 0) {
- destinationUri = SaveCopyTask.makeAndInsertUri(this, mSourceUri);
+ destinationUri = SaveImage.makeAndInsertUri(this, mSourceUri);
if (destinationUri != null) {
flags |= DO_EXTRA_OUTPUT;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index c0a6c13..1d3a10d 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -20,8 +20,10 @@
import android.util.Log;
import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
import com.android.gallery3d.filtershow.pipeline.ImagePreset;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;
@@ -30,6 +32,11 @@
protected HashMap<String, FilterRepresentation> mRepresentationLookup = null;
private static final String LOGTAG = "BaseFiltersManager";
+ protected ArrayList<FilterRepresentation> mLooks = new ArrayList<FilterRepresentation>();
+ protected ArrayList<FilterRepresentation> mBorders = new ArrayList<FilterRepresentation>();
+ protected ArrayList<FilterRepresentation> mTools = new ArrayList<FilterRepresentation>();
+ protected ArrayList<FilterRepresentation> mEffects = new ArrayList<FilterRepresentation>();
+
protected void init() {
mFilters = new HashMap<Class, ImageFilter>();
mRepresentationLookup = new HashMap<String, FilterRepresentation>();
@@ -133,11 +140,27 @@
filters.add(ImageFilterGeometry.class);
}
- public void addBorders(Context context, Vector<FilterRepresentation> representations) {
+ public ArrayList<FilterRepresentation> getLooks() {
+ return mLooks;
+ }
+
+ public ArrayList<FilterRepresentation> getBorders() {
+ return mBorders;
+ }
+
+ public ArrayList<FilterRepresentation> getTools() {
+ return mTools;
+ }
+
+ public ArrayList<FilterRepresentation> getEffects() {
+ return mEffects;
+ }
+
+ public void addBorders(Context context) {
}
- public void addLooks(Context context, Vector<FilterRepresentation> representations) {
+ public void addLooks(Context context) {
int[] drawid = {
R.drawable.filtershow_fx_0005_punch,
R.drawable.filtershow_fx_0000_vintage,
@@ -175,37 +198,72 @@
"LUT3D_XPROCESS"
};
+ FilterFxRepresentation nullFx =
+ new FilterFxRepresentation(context.getString(R.string.none),
+ 0, R.string.none);
+ mLooks.add(nullFx);
+
for (int i = 0; i < drawid.length; i++) {
FilterFxRepresentation fx = new FilterFxRepresentation(
context.getString(fxNameid[i]), drawid[i], fxNameid[i]);
fx.setSerializationName(serializationNames[i]);
- representations.add(fx);
+ mLooks.add(fx);
addRepresentation(fx);
}
}
- public void addEffects(Vector<FilterRepresentation> representations) {
- representations.add(getRepresentation(ImageFilterTinyPlanet.class));
- representations.add(getRepresentation(ImageFilterWBalance.class));
- representations.add(getRepresentation(ImageFilterExposure.class));
- representations.add(getRepresentation(ImageFilterVignette.class));
- representations.add(getRepresentation(ImageFilterContrast.class));
- representations.add(getRepresentation(ImageFilterShadows.class));
- representations.add(getRepresentation(ImageFilterHighlights.class));
- representations.add(getRepresentation(ImageFilterVibrance.class));
- representations.add(getRepresentation(ImageFilterSharpen.class));
- representations.add(getRepresentation(ImageFilterCurves.class));
- representations.add(getRepresentation(ImageFilterHue.class));
- representations.add(getRepresentation(ImageFilterSaturated.class));
- representations.add(getRepresentation(ImageFilterBwFilter.class));
- representations.add(getRepresentation(ImageFilterNegative.class));
- representations.add(getRepresentation(ImageFilterEdge.class));
- representations.add(getRepresentation(ImageFilterKMeans.class));
+ public void addEffects() {
+ mEffects.add(getRepresentation(ImageFilterTinyPlanet.class));
+ mEffects.add(getRepresentation(ImageFilterWBalance.class));
+ mEffects.add(getRepresentation(ImageFilterExposure.class));
+ mEffects.add(getRepresentation(ImageFilterVignette.class));
+ mEffects.add(getRepresentation(ImageFilterContrast.class));
+ mEffects.add(getRepresentation(ImageFilterShadows.class));
+ mEffects.add(getRepresentation(ImageFilterHighlights.class));
+ mEffects.add(getRepresentation(ImageFilterVibrance.class));
+ mEffects.add(getRepresentation(ImageFilterSharpen.class));
+ mEffects.add(getRepresentation(ImageFilterCurves.class));
+ mEffects.add(getRepresentation(ImageFilterHue.class));
+ mEffects.add(getRepresentation(ImageFilterSaturated.class));
+ mEffects.add(getRepresentation(ImageFilterBwFilter.class));
+ mEffects.add(getRepresentation(ImageFilterNegative.class));
+ mEffects.add(getRepresentation(ImageFilterEdge.class));
+ mEffects.add(getRepresentation(ImageFilterKMeans.class));
}
- public void addTools(Vector<FilterRepresentation> representations) {
- representations.add(getRepresentation(ImageFilterRedEye.class));
- representations.add(getRepresentation(ImageFilterDraw.class));
+ public void addTools(Context context) {
+ GeometryMetadata geo = new GeometryMetadata();
+ int[] editorsId = geo.getEditorIds();
+
+ int[] textId = {
+ R.string.crop,
+ R.string.straighten,
+ R.string.rotate,
+ R.string.mirror
+ };
+
+ int[] overlayId = {
+ R.drawable.filtershow_button_geometry_crop,
+ R.drawable.filtershow_button_geometry_straighten,
+ R.drawable.filtershow_button_geometry_rotate,
+ R.drawable.filtershow_button_geometry_flip
+ };
+
+ for (int i = 0; i < editorsId.length; i++) {
+ int editorId = editorsId[i];
+ GeometryMetadata geometry = new GeometryMetadata(geo);
+ geometry.setEditorId(editorId);
+ geometry.setTextId(textId[i]);
+ geometry.setOverlayId(overlayId[i]);
+ geometry.setOverlayOnly(true);
+ if (geometry.getTextId() != 0) {
+ geometry.setName(context.getString(geometry.getTextId()));
+ }
+ mTools.add(geometry);
+ }
+
+ mTools.add(getRepresentation(ImageFilterRedEye.class));
+ mTools.add(getRepresentation(ImageFilterDraw.class));
}
public void setFilterResources(Resources resources) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 2da358c..1a304db 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -40,7 +40,7 @@
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.pipeline.ImagePreset;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.tools.SaveImage;
import java.io.File;
@@ -395,7 +395,7 @@
}
public void saveImage(FilterShowActivity filterShowActivity, File file) {
- SaveCopyTask.saveImage(getImagePreset(), filterShowActivity, file);
+ SaveImage.saveImage(getImagePreset(), filterShowActivity, file);
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
index ed09fb1..01fe3c1 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
@@ -482,6 +482,9 @@
}
public void needsUpdatePartialPreview() {
+ if (mPreset == null) {
+ return;
+ }
if (!mPreset.canDoPartialRendering()) {
invalidatePartialPreview();
return;
diff --git a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
index a7580a8..535d02f 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
@@ -16,7 +16,7 @@
package com.android.gallery3d.filtershow.pipeline;
-import android.app.Activity;
+import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.support.v8.renderscript.Allocation;
@@ -68,7 +68,7 @@
return sRS;
}
- public static synchronized void createRenderscriptContext(Activity context) {
+ public static synchronized void createRenderscriptContext(Context context) {
if (sRS != null) {
Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
destroyRenderScriptContext();
diff --git a/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java
index a302b19..0e9b83d 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java
@@ -21,7 +21,6 @@
import android.os.Process;
import android.util.Log;
-import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
index 78a4d21..2b9e370 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
@@ -55,7 +55,7 @@
private boolean mPartialRendering = false;
private Rect mPartialRenderingBounds;
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
public ImagePreset() {
}
@@ -607,7 +607,7 @@
if (DEBUG) {
Log.v(LOGTAG, "Serialization: " + sname);
if (sname == null) {
- Log.v(LOGTAG, "Serialization: " + filter);
+ Log.v(LOGTAG, "Serialization name null for filter: " + filter);
}
}
writer.name(sname);
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
new file mode 100644
index 0000000..e93ec16
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
@@ -0,0 +1,123 @@
+/*
+ * 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.gallery3d.filtershow.pipeline;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.filters.FiltersManager;
+import com.android.gallery3d.filtershow.tools.SaveImage;
+
+import java.io.File;
+
+public class ImageSavingTask extends ProcessingTask {
+ private ProcessingService mProcessingService;
+
+ static class SaveRequest implements Request {
+ Uri sourceUri;
+ Uri selectedUri;
+ File destinationFile;
+ ImagePreset preset;
+ }
+
+ static class UpdateBitmap implements Update {
+ Bitmap bitmap;
+ }
+
+ static class UpdateProgress implements Update {
+ int max;
+ int current;
+ }
+
+ static class URIResult implements Result {
+ Uri uri;
+ }
+
+ public ImageSavingTask(ProcessingService service) {
+ mProcessingService = service;
+ }
+
+ public void saveImage(Uri sourceUri, Uri selectedUri,
+ File destinationFile, ImagePreset preset) {
+ SaveRequest request = new SaveRequest();
+ request.sourceUri = sourceUri;
+ request.selectedUri = selectedUri;
+ request.destinationFile = destinationFile;
+ request.preset = preset;
+ postRequest(request);
+ }
+
+ public Result doInBackground(Request message) {
+ SaveRequest request = (SaveRequest) message;
+ Uri sourceUri = request.sourceUri;
+ Uri selectedUri = request.selectedUri;
+ File destinationFile = request.destinationFile;
+ ImagePreset preset = request.preset;
+
+ // We create a small bitmap showing the result that we can
+ // give to the notification
+ UpdateBitmap updateBitmap = new UpdateBitmap();
+ updateBitmap.bitmap = createNotificationBitmap(sourceUri, preset);
+ postUpdate(updateBitmap);
+
+ SaveImage saveImage = new SaveImage(mProcessingService, sourceUri,
+ selectedUri, destinationFile,
+ new SaveImage.Callback() {
+ @Override
+ public void onProgress(int max, int current) {
+ UpdateProgress updateProgress = new UpdateProgress();
+ updateProgress.max = max;
+ updateProgress.current = current;
+ postUpdate(updateProgress);
+ }
+ });
+
+ Uri uri = saveImage.processAndSaveImage(preset);
+ URIResult result = new URIResult();
+ result.uri = uri;
+ return result;
+ }
+
+ @Override
+ public void onResult(Result message) {
+ URIResult result = (URIResult) message;
+ mProcessingService.completeSaveImage(result.uri);
+ }
+
+ @Override
+ public void onUpdate(Update message) {
+ if (message instanceof UpdateBitmap) {
+ Bitmap bitmap = ((UpdateBitmap) message).bitmap;
+ mProcessingService.updateNotificationWithBitmap(bitmap);
+ }
+ if (message instanceof UpdateProgress) {
+ UpdateProgress progress = (UpdateProgress) message;
+ mProcessingService.updateProgress(progress.max, progress.current);
+ }
+ }
+
+ private Bitmap createNotificationBitmap(Uri sourceUri, ImagePreset preset) {
+ int notificationBitmapSize = Resources.getSystem().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ Bitmap bitmap = ImageLoader.loadConstrainedBitmap(sourceUri, getContext(),
+ notificationBitmapSize, null, true);
+ CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(), "Thumb");
+ return pipeline.renderFinalImage(bitmap, preset);
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
new file mode 100644
index 0000000..0320247
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
@@ -0,0 +1,217 @@
+/*
+ * 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.gallery3d.filtershow.pipeline;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.filters.FiltersManager;
+import com.android.gallery3d.filtershow.filters.ImageFilter;
+import com.android.gallery3d.filtershow.tools.SaveImage;
+
+import java.io.File;
+
+public class ProcessingService extends Service {
+ private static final String LOGTAG = "ProcessingService";
+ private static final boolean SHOW_IMAGE = false;
+ private int mNotificationId;
+ private NotificationManager mNotifyMgr = null;
+ private Notification.Builder mBuilder = null;
+
+ private static final String PRESET = "preset";
+ private static final String SOURCE_URI = "sourceUri";
+ private static final String SELECTED_URI = "selectedUri";
+ private static final String DESTINATION_FILE = "destinationFile";
+ private static final String SAVING = "saving";
+
+ private ProcessingTaskController mProcessingTaskController;
+ private ImageSavingTask mImageSavingTask;
+
+ private final IBinder mBinder = new LocalBinder();
+ private FilterShowActivity mFiltershowActivity;
+
+ private boolean mSaving = false;
+ private boolean mNeedsAlive = false;
+
+ public void setFiltershowActivity(FilterShowActivity filtershowActivity) {
+ mFiltershowActivity = filtershowActivity;
+ }
+
+ public class LocalBinder extends Binder {
+ public ProcessingService getService() {
+ return ProcessingService.this;
+ }
+ }
+
+ public static Intent getSaveIntent(Context context, ImagePreset preset, File destination,
+ Uri selectedImageUri, Uri sourceImageUri) {
+ Intent processIntent = new Intent(context, ProcessingService.class);
+ processIntent.putExtra(ProcessingService.SOURCE_URI,
+ sourceImageUri.toString());
+ processIntent.putExtra(ProcessingService.SELECTED_URI,
+ selectedImageUri.toString());
+ if (destination != null) {
+ processIntent.putExtra(ProcessingService.DESTINATION_FILE, destination.toString());
+ }
+ processIntent.putExtra(ProcessingService.PRESET,
+ preset.getJsonString(context.getString(R.string.saved)));
+ processIntent.putExtra(ProcessingService.SAVING, true);
+ return processIntent;
+ }
+
+
+ @Override
+ public void onCreate() {
+ mProcessingTaskController = new ProcessingTaskController(this);
+ mImageSavingTask = new ImageSavingTask(this);
+ mProcessingTaskController.add(mImageSavingTask);
+ setupPipeline();
+ }
+
+ @Override
+ public void onDestroy() {
+ tearDownPipeline();
+ mProcessingTaskController.quit();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ mNeedsAlive = true;
+ if (intent != null && intent.getBooleanExtra(SAVING, false)) {
+ // we save using an intent to keep the service around after the
+ // activity has been destroyed.
+ String presetJson = intent.getStringExtra(PRESET);
+ String source = intent.getStringExtra(SOURCE_URI);
+ String selected = intent.getStringExtra(SELECTED_URI);
+ String destination = intent.getStringExtra(DESTINATION_FILE);
+ Uri sourceUri = Uri.parse(source);
+ Uri selectedUri = null;
+ if (selected != null) {
+ selectedUri = Uri.parse(selected);
+ }
+ File destinationFile = null;
+ if (destination != null) {
+ destinationFile = new File(destination);
+ }
+ ImagePreset preset = new ImagePreset();
+ preset.readJsonFromString(presetJson);
+ mNeedsAlive = false;
+ mSaving = true;
+ handleSaveRequest(sourceUri, selectedUri, destinationFile, preset);
+ }
+ return START_REDELIVER_INTENT;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public void onStart() {
+ mNeedsAlive = true;
+ if (!mSaving && mFiltershowActivity != null) {
+ mFiltershowActivity.updateUIAfterServiceStarted();
+ }
+ }
+
+ public void handleSaveRequest(Uri sourceUri, Uri selectedUri,
+ File destinationFile, ImagePreset preset) {
+ mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+ mNotificationId++;
+
+ mBuilder =
+ new Notification.Builder(this)
+ .setSmallIcon(R.drawable.filtershow_button_fx)
+ .setContentTitle(getString(R.string.filtershow_notification_label))
+ .setContentText(getString(R.string.filtershow_notification_message));
+
+ startForeground(mNotificationId, mBuilder.build());
+
+ updateProgress(SaveImage.MAX_PROCESSING_STEPS, 0);
+
+ // Process the image
+
+ mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile, preset);
+ }
+
+ public void updateNotificationWithBitmap(Bitmap bitmap) {
+ mBuilder.setLargeIcon(bitmap);
+ mNotifyMgr.notify(mNotificationId, mBuilder.build());
+ }
+
+ public void updateProgress(int max, int current) {
+ mBuilder.setProgress(max, current, false);
+ mNotifyMgr.notify(mNotificationId, mBuilder.build());
+ }
+
+ public void completeSaveImage(Uri result) {
+ if (SHOW_IMAGE) {
+ // TODO: we should update the existing image in Gallery instead
+ Intent viewImage = new Intent(Intent.ACTION_VIEW, result);
+ viewImage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(viewImage);
+ }
+ stopForeground(true);
+ stopSelf();
+ if (mNeedsAlive) {
+ // If the app has been restarted while we were saving...
+ mFiltershowActivity.updateUIAfterServiceStarted();
+ } else if (mFiltershowActivity.isSimpleEditAction()) {
+ // terminate now
+ mFiltershowActivity.completeSaveImage(result);
+ }
+ }
+
+ private void setupPipeline() {
+ Resources res = getResources();
+ FiltersManager.setResources(res);
+ CachingPipeline.createRenderscriptContext(this);
+
+ FiltersManager filtersManager = FiltersManager.getManager();
+ filtersManager.addLooks(this);
+ filtersManager.addBorders(this);
+ filtersManager.addTools(this);
+ filtersManager.addEffects();
+ }
+
+ private void tearDownPipeline() {
+ FilteringPipeline.getPipeline().turnOnPipeline(false);
+ FilteringPipeline.reset();
+ ImageFilter.resetStatics();
+ FiltersManager.getPreviewManager().freeRSFilterScripts();
+ FiltersManager.getManager().freeRSFilterScripts();
+ FiltersManager.getHighresManager().freeRSFilterScripts();
+ FiltersManager.reset();
+ CachingPipeline.destroyRenderScriptContext();
+ }
+
+ static {
+ System.loadLibrary("jni_filtershow_filters");
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java
new file mode 100644
index 0000000..c3687ee
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java
@@ -0,0 +1,72 @@
+/*
+ * 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.gallery3d.filtershow.pipeline;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+public abstract class ProcessingTask {
+ private ProcessingTaskController mTaskController;
+ private Handler mProcessingHandler;
+ private Handler mResultHandler;
+ private int mType;
+
+ static interface Request {}
+ static interface Update {}
+ static interface Result {}
+
+ public void postRequest(Request message) {
+ Message msg = mProcessingHandler.obtainMessage(mType);
+ msg.obj = message;
+ mProcessingHandler.sendMessage(msg);
+ }
+
+ public void postUpdate(Update message) {
+ Message msg = mResultHandler.obtainMessage(mType);
+ msg.obj = message;
+ msg.arg1 = ProcessingTaskController.UPDATE;
+ mResultHandler.sendMessage(msg);
+ }
+
+ public void processRequest(Request message) {
+ Object result = doInBackground(message);
+ Message msg = mResultHandler.obtainMessage(mType);
+ msg.obj = result;
+ msg.arg1 = ProcessingTaskController.RESULT;
+ mResultHandler.sendMessage(msg);
+ }
+
+ public void added(ProcessingTaskController taskController) {
+ mTaskController = taskController;
+ mResultHandler = taskController.getResultHandler();
+ mProcessingHandler = taskController.getProcessingHandler();
+ mType = taskController.getReservedType();
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public Context getContext() {
+ return mTaskController.getContext();
+ }
+
+ public abstract Result doInBackground(Request message);
+ public abstract void onResult(Result message);
+ public void onUpdate(Update message) {}
+}
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java
new file mode 100644
index 0000000..218ea63
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java
@@ -0,0 +1,98 @@
+/*
+ * 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.gallery3d.filtershow.pipeline;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.HashMap;
+
+public class ProcessingTaskController implements Handler.Callback {
+ private static final String LOGTAG = "ProcessingTaskController";
+
+ private Context mContext;
+ private HandlerThread mHandlerThread = null;
+ private Handler mProcessingHandler = null;
+ private int mCurrentType;
+ private HashMap<Integer, ProcessingTask> mTasks = new HashMap<Integer, ProcessingTask>();
+
+ public final static int RESULT = 1;
+ public final static int UPDATE = 2;
+
+ private final Handler mResultHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ ProcessingTask task = mTasks.get(msg.what);
+ if (task != null) {
+ if (msg.arg1 == RESULT) {
+ task.onResult((ProcessingTask.Result) msg.obj);
+ } else if (msg.arg1 == UPDATE) {
+ task.onUpdate((ProcessingTask.Update) msg.obj);
+ } else {
+ Log.w(LOGTAG, "received unknown message! " + msg.arg1);
+ }
+ }
+ }
+ };
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ ProcessingTask task = mTasks.get(msg.what);
+ if (task != null) {
+ task.processRequest((ProcessingTask.Request) msg.obj);
+ return true;
+ }
+ return false;
+ }
+
+ public ProcessingTaskController(Context context) {
+ mContext = context;
+ mHandlerThread = new HandlerThread("ProcessingTaskController",
+ android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ mHandlerThread.start();
+ mProcessingHandler = new Handler(mHandlerThread.getLooper(), this);
+ }
+
+ public Handler getProcessingHandler() {
+ return mProcessingHandler;
+ }
+
+ public Handler getResultHandler() {
+ return mResultHandler;
+ }
+
+ public int getReservedType() {
+ return mCurrentType++;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public void add(ProcessingTask task) {
+ task.added(this);
+ mTasks.put(task.getType(), task);
+ }
+
+ public void quit() {
+ mHandlerThread.quit();
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveImage.java
similarity index 90%
rename from src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
rename to src/com/android/gallery3d/filtershow/tools/SaveImage.java
index dcf0ae1..9b13af1 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveImage.java
@@ -19,18 +19,18 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
-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.provider.MediaStore.Images.Media;
import android.util.Log;
+import com.android.gallery3d.R;
+import com.android.gallery3d.app.PhotoPage;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.filtershow.FilterShowActivity;
@@ -39,6 +39,7 @@
import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
import com.android.gallery3d.filtershow.pipeline.ImagePreset;
+import com.android.gallery3d.filtershow.pipeline.ProcessingService;
import com.android.gallery3d.util.UsageStatistics;
import com.android.gallery3d.util.XmpUtilHelper;
@@ -52,17 +53,16 @@
import java.util.TimeZone;
/**
- * Asynchronous task for saving edited photo as a new copy.
+ * Handles saving edited photo
*/
-public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
-
- private static final String LOGTAG = "SaveCopyTask";
+public class SaveImage {
+ private static final String LOGTAG = "SaveImage";
/**
- * Callback for the completed asynchronous task.
+ * Callback for updates
*/
public interface Callback {
- void onComplete(Uri result);
+ void onProgress(int max, int current);
}
public interface ContentResolverQueryCallback {
@@ -85,6 +85,10 @@
private final Callback mCallback;
private final File mDestinationFile;
private final Uri mSelectedImageUri;
+
+ private int mCurrentProcessingStep = 1;
+
+ public static final int MAX_PROCESSING_STEPS = 6;
public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
// In order to support the new edit-save behavior such that user won't see
@@ -129,8 +133,8 @@
* @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) {
+ public SaveImage(Context context, Uri sourceUri, Uri selectedImageUri,
+ File destination, Callback callback) {
mContext = context;
mSourceUri = sourceUri;
mCallback = callback;
@@ -144,10 +148,10 @@
}
public static File getFinalSaveDirectory(Context context, Uri sourceUri) {
- File saveDirectory = SaveCopyTask.getSaveDirectory(context, sourceUri);
+ File saveDirectory = SaveImage.getSaveDirectory(context, sourceUri);
if ((saveDirectory == null) || !saveDirectory.canWrite()) {
saveDirectory = new File(Environment.getExternalStorageDirectory(),
- SaveCopyTask.DEFAULT_SAVE_DIRECTORY);
+ SaveImage.DEFAULT_SAVE_DIRECTORY);
}
// Create the directory if it doesn't exist
if (!saveDirectory.exists())
@@ -275,17 +279,7 @@
return ret;
}
- /**
- * The task should be executed with one given bitmap to be saved.
- */
- @Override
- protected Uri doInBackground(ImagePreset... params) {
- // TODO: Support larger dimensions for photo saving.
- if (params[0] == null || mSourceUri == null || mSelectedImageUri == null) {
- return null;
- }
-
- ImagePreset preset = params[0];
+ private Uri resetToOriginalImageIfNeeded(ImagePreset preset) {
Uri uri = null;
if (!preset.hasModifications()) {
// This can happen only when preset has no modification but save
@@ -298,12 +292,32 @@
// create a local copy as usual.
if (srcFile != null) {
srcFile.renameTo(mDestinationFile);
- uri = SaveCopyTask.insertContent(mContext, mSelectedImageUri, mDestinationFile,
+ uri = SaveImage.insertContent(mContext, mSelectedImageUri, mDestinationFile,
System.currentTimeMillis());
removeSelectedImage();
- return uri;
}
}
+ return uri;
+ }
+
+ private void resetProgress() {
+ mCurrentProcessingStep = 0;
+ }
+
+ private void updateProgress() {
+ if (mCallback != null) {
+ mCallback.onProgress(MAX_PROCESSING_STEPS, ++mCurrentProcessingStep);
+ }
+ }
+
+ public Uri processAndSaveImage(ImagePreset preset) {
+
+ Uri uri = resetToOriginalImageIfNeeded(preset);
+ if (uri != null) {
+ return null;
+ }
+
+ resetProgress();
boolean noBitmap = true;
int num_tries = 0;
@@ -321,15 +335,19 @@
// Stopgap fix for low-memory devices.
while (noBitmap) {
try {
+ updateProgress();
// Try to do bitmap operations, downsample if low-memory
Bitmap bitmap = ImageLoader.loadOrientedBitmapWithBackouts(mContext, newSourceUri,
sampleSize);
if (bitmap == null) {
return null;
}
+ updateProgress();
CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(),
"Saving");
+
bitmap = pipeline.renderFinalImage(bitmap, preset);
+ updateProgress();
Object xmp = getPanoramaXMPData(mSelectedImageUri, preset);
ExifInterface exif = getExifData(mSelectedImageUri);
@@ -347,28 +365,34 @@
// If we succeed in writing the bitmap as a jpeg, return a uri.
if (putExifData(mDestinationFile, exif, bitmap)) {
putPanoramaXMPData(mDestinationFile, xmp);
- uri = SaveCopyTask.insertContent(mContext, mSelectedImageUri, mDestinationFile,
+ uri = SaveImage.insertContent(mContext, mSelectedImageUri, mDestinationFile,
time);
}
+ updateProgress();
// mDestinationFile will save the newSourceUri info in the XMP.
XmpPresets.writeFilterXMP(mContext, newSourceUri, mDestinationFile, preset);
+ updateProgress();
// Since we have a new image inserted to media store, we can
// safely remove the old one which is selected by the user.
+ // TODO: we should fix that, do an update instead of insert+remove,
+ // as well as asking Gallery to update its cached version of the image
if (USE_AUX_DIR) {
removeSelectedImage();
}
+ updateProgress();
noBitmap = false;
UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
"SaveComplete", null);
- } catch (java.lang.OutOfMemoryError e) {
+ } catch (OutOfMemoryError e) {
// Try 5 times before failing for good.
if (++num_tries >= 5) {
throw e;
}
System.gc();
sampleSize *= 2;
+ resetProgress();
}
}
return uri;
@@ -434,13 +458,6 @@
return auxDiretory;
}
- @Override
- protected void onPostExecute(Uri result) {
- if (mCallback != null) {
- mCallback.onComplete(result);
- }
- }
-
public static Uri makeAndInsertUri(Context context, Uri sourceUri) {
long time = System.currentTimeMillis();
String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(time));
@@ -452,19 +469,19 @@
public static void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
File destination) {
Uri selectedImageUri = filterShowActivity.getSelectedImageUri();
- new SaveCopyTask(filterShowActivity, MasterImage.getImage().getUri(), selectedImageUri,
- destination,
- new SaveCopyTask.Callback() {
+ Uri sourceImageUri = MasterImage.getImage().getUri();
- @Override
- public void onComplete(Uri result) {
- filterShowActivity.completeSaveImage(result);
- }
+ Intent processIntent = ProcessingService.getSaveIntent(filterShowActivity, preset,
+ destination, selectedImageUri, sourceImageUri);
- }).execute(preset);
+ filterShowActivity.startService(processIntent);
+
+ if (!filterShowActivity.isSimpleEditAction()) {
+ // terminate for now
+ filterShowActivity.completeSaveImage(selectedImageUri);
+ }
}
-
public static void querySource(Context context, Uri sourceUri, String[] projection,
ContentResolverQueryCallback callback) {
ContentResolver contentResolver = context.getContentResolver();
@@ -586,8 +603,8 @@
ImageColumns.DATE_TAKEN,
ImageColumns.LATITUDE, ImageColumns.LONGITUDE,
};
- SaveCopyTask.querySource(context, sourceUri, projection,
- new SaveCopyTask.ContentResolverQueryCallback() {
+ SaveImage.querySource(context, sourceUri, projection,
+ new SaveImage.ContentResolverQueryCallback() {
@Override
public void onCursorResult(Cursor cursor) {
diff --git a/src/com/android/gallery3d/util/SaveVideoFileUtils.java b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
index da0970b..10c41de 100644
--- a/src/com/android/gallery3d/util/SaveVideoFileUtils.java
+++ b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
@@ -25,7 +25,7 @@
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video.VideoColumns;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask.ContentResolverQueryCallback;
+import com.android.gallery3d.filtershow.tools.SaveImage.ContentResolverQueryCallback;
import java.io.File;
import java.sql.Date;
diff --git a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
index c5a6435..d4035cd 100644
--- a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
+++ b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
@@ -22,9 +22,6 @@
import com.android.gallery3d.R;
-import java.util.HashMap;
-import java.util.Vector;
-
public class FiltersManager extends BaseFiltersManager {
private static FiltersManager sInstance = null;
private static FiltersManager sPreviewInstance = null;
@@ -49,7 +46,7 @@
}
@Override
- public void addBorders(Context context, Vector<FilterRepresentation> representations) {
+ public void addBorders(Context context) {
// Do not localize
String[] serializationNames = {
@@ -66,55 +63,59 @@
"FRAME_CREAM_ROUNDED"
};
+ // The "no border" implementation
int i = 0;
+ FilterRepresentation rep = new FilterImageBorderRepresentation(0);
+ mBorders.add(rep);
+
// Regular borders
- FilterRepresentation rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5);
+ rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize,
mImageBorderSize);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize,
mImageBorderSize);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
int creamColor = Color.argb(255, 237, 237, 227);
rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize,
mImageBorderSize);
rep.setSerializationName(serializationNames[i++]);
- representations.add(rep);
+ mBorders.add(rep);
}
public static FiltersManager getHighresManager() {