OSDN Git Service

Merge "add Grad filter" into gb-ub-photos-carlsbad
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / FilterShowActivity.java
index bdcd83c..8bbdd0c 100644 (file)
@@ -19,28 +19,34 @@ package com.android.gallery3d.filtershow;
 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;
 import android.view.View.OnClickListener;
+import android.view.ViewPropertyAnimator;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -50,20 +56,19 @@ import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
 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.cache.CachingPipeline;
-import com.android.gallery3d.filtershow.cache.FilteringPipeline;
+import com.android.gallery3d.filtershow.editors.EditorGrad;
+import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
 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;
@@ -71,28 +76,31 @@ import com.android.gallery3d.filtershow.editors.EditorRotate;
 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;
+import com.android.gallery3d.filtershow.history.HistoryManager;
+import com.android.gallery3d.filtershow.history.HistoryItem;
 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
 import com.android.gallery3d.filtershow.imageshow.ImageShow;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
-import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.imageshow.Spline;
+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;
-import com.android.gallery3d.filtershow.ui.Spline;
 import com.android.gallery3d.util.GalleryUtils;
+import com.android.gallery3d.util.UsageStatistics;
 import com.android.photos.data.GalleryBitmapPool;
 
 import java.io.File;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Vector;
 
 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
@@ -105,7 +113,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
 
     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
-    private ImageLoader mImageLoader = null;
     private ImageShow mImageShow = null;
 
     private View mSaveButton = null;
@@ -134,12 +141,83 @@ 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;
     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);
@@ -151,19 +229,16 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         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);
     }
 
     public boolean isShowingImageStatePanel() {
@@ -209,7 +284,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
             View container = mainPanel.getView().findViewById(R.id.category_panel_container);
             View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
             int panelHeight = container.getHeight() + bottom.getHeight();
-            mainPanel.getView().animate().translationY(panelHeight).withEndAction(showEditor).start();
+            ViewPropertyAnimator anim = mainPanel.getView().animate();
+            anim.translationY(panelHeight).start();
+            final Handler handler = new Handler();
+            handler.postDelayed(showEditor, anim.getDuration());
         } else {
             showEditor.run();
         }
@@ -236,26 +314,25 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         setupEditors();
 
         mEditorPlaceHolder.hide();
+        mImageShow.bindAsImageLoadListener();
 
-        mImageShow.setImageLoader(mImageLoader);
+        setupStatePanel();
+    }
 
-        fillFx();
+    public void fillCategories() {
+        fillLooks();
         fillBorders();
-        fillGeometry();
-        fillFilters();
-
-        setupStatePanel();
+        fillTools();
+        fillEffects();
     }
 
     public void setupStatePanel() {
-        mImageLoader.setAdapter(mMasterImage.getHistory());
+        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) {
@@ -265,28 +342,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         }
     }
 
-    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));
@@ -300,12 +358,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();
         }
@@ -315,10 +374,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
         EditorManager.addEditors(mEditorPlaceHolder);
         mEditorPlaceHolder.setOldViews(mImageViews);
-        mEditorPlaceHolder.setImageLoader(mImageLoader);
     }
 
     private void fillEditors() {
+        mEditorPlaceHolder.addEditor(new EditorGrad());
         mEditorPlaceHolder.addEditor(new EditorDraw());
         mEditorPlaceHolder.addEditor(new BasicEditor());
         mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
@@ -331,13 +390,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     }
 
     private void setDefaultValues() {
-        ImageFilter.setActivityForMemoryToasts(this);
-
         Resources res = getResources();
-        FiltersManager.setResources(res);
-
-        CategoryView.setMargin((int) getPixelsFromDip(8));
-        CategoryView.setTextSize((int) getPixelsFromDip(16));
 
         // TODO: get those values from XML.
         FramedTextButton.setTextSize((int) getPixelsFromDip(14));
@@ -366,19 +419,14 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     }
 
     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);
-            filter.setScrName(getString(R.string.borders));
+            FilterRepresentation filter = borders.get(i);
+            filter.setName(getString(R.string.borders));
             if (i == 0) {
-                filter.setScrName(getString(R.string.none));
+                filter.setName(getString(R.string.none));
             }
         }
 
@@ -414,7 +462,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         ImagePreset oldPreset = MasterImage.getImage().getPreset();
         ImagePreset copy = new ImagePreset(oldPreset);
         copy.removeFilter(filterRepresentation);
-        MasterImage.getImage().setPreset(copy, true);
+        MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
             FilterRepresentation lastRepresentation = copy.getLastRepresentation();
             MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
@@ -434,14 +482,15 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         if (representation == null) {
             copy.addFilter(filterRepresentation);
         } else {
-            if (filterRepresentation.allowsMultipleInstances()) {
-                representation.updateTempParametersFrom(filterRepresentation);
-                copy.setHistoryName(filterRepresentation.getName());
-                representation.synchronizeRepresentation();
+            if (filterRepresentation.allowsSingleInstanceOnly()) {
+                // Don't just update the filter representation. Centralize the
+                // logic in the addFilter(), such that we can keep "None" as
+                // null.
+                copy.removeFilter(representation);
+                copy.addFilter(filterRepresentation);
             }
-            filterRepresentation = representation;
         }
-        MasterImage.getImage().setPreset(copy, true);
+        MasterImage.getImage().setPreset(copy, filterRepresentation, true);
         MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
     }
 
@@ -449,7 +498,15 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         if (representation == null) {
             return;
         }
-        useFilterRepresentation(representation);
+
+        // TODO: this check is needed because the GeometryMetadata doesn't quite
+        // follow the same pattern as the other filters to update/sync their values.
+        // We thus need to not call useFilterRepresentation() for now, as it
+        // would override the current Geometry. Once GeometryMetadata is fixed,
+        // let's remove the check and call useFilterRepresentation all the time.
+        if (!(representation instanceof GeometryMetadata)) {
+            useFilterRepresentation(representation);
+        }
 
         // show representation
         Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
@@ -468,6 +525,45 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         return mCurrentPanel;
     }
 
+    public void updateCategories() {
+        ImagePreset preset = mMasterImage.getPreset();
+        mCategoryLooksAdapter.reflectImagePreset(preset);
+        mCategoryBordersAdapter.reflectImagePreset(preset);
+    }
+
+    private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            MasterImage master = MasterImage.getImage();
+            Rect originalBounds = master.getOriginalBounds();
+            if (master.supportsHighRes()) {
+                int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
+                if (highresPreviewSize > originalBounds.width()) {
+                    highresPreviewSize = originalBounds.width();
+                }
+                Rect bounds = new Rect();
+                Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
+                        master.getActivity(), highresPreviewSize,
+                        master.getOrientation(), bounds);
+                master.setOriginalBounds(bounds);
+                master.setOriginalBitmapHighres(originalHires);
+                mBoundService.setOriginalBitmapHighres(originalHires);
+                master.warnListeners();
+            }
+            return true;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean result) {
+            Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
+            if (highresBitmap != null) {
+                float highResPreviewScale = (float) highresBitmap.getWidth()
+                        / (float) MasterImage.getImage().getOriginalBounds().width();
+                mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
+            }
+        }
+    }
+
     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
         int mBitmapSize;
 
@@ -477,10 +573,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
 
         @Override
         protected Boolean doInBackground(Uri... params) {
-            if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
+            if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
                 return false;
             }
-            publishProgress(mImageLoader.queryLightCycle360());
+            publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
             return true;
         }
 
@@ -506,25 +602,24 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
                 cannotLoadImage();
             }
 
+            if (null == CachingPipeline.getRenderScriptContext()){
+                Log.v(LOGTAG,"RenderScript context destroyed during load");
+                return;
+            }
             final View loading = findViewById(R.id.loading);
             loading.setVisibility(View.GONE);
             final View imageShow = findViewById(R.id.imageShow);
             imageShow.setVisibility(View.VISIBLE);
 
-            Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
-            FilteringPipeline pipeline = FilteringPipeline.getPipeline();
-            pipeline.setOriginal(largeBitmap);
-            float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
-            pipeline.setPreviewScaleFactor(previewScale);
-            Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
-            if (highresBitmap != null) {
-                float highResPreviewScale = (float) highresBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
-                pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
-            }
+            Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
+            mBoundService.setOriginalBitmap(largeBitmap);
+
+            float previewScale = (float) largeBitmap.getWidth()
+                    / (float) MasterImage.getImage().getOriginalBounds().width();
+            mBoundService.setPreviewScaleFactor(previewScale);
             if (!mShowingTinyPlanet) {
                 mCategoryFiltersAdapter.removeTinyPlanet();
             }
-            pipeline.turnOnPipeline(true);
             MasterImage.getImage().setOriginalGeometry(largeBitmap);
             mCategoryLooksAdapter.imageLoaded();
             mCategoryBordersAdapter.imageLoaded();
@@ -533,7 +628,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
             mLoadBitmapTask = null;
 
             if (mOriginalPreset != null) {
-                MasterImage.getImage().setPreset(mOriginalPreset, true);
+                MasterImage.getImage().setLoadedPreset(mOriginalPreset);
+                MasterImage.getImage().setPreset(mOriginalPreset,
+                        mOriginalPreset.getLastRepresentation(), true);
                 mOriginalPreset = null;
             }
 
@@ -541,6 +638,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
             }
             mLoading = false;
+            MasterImage.getImage().notifyGeometryChange();
+            LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
+            highresLoad.execute();
             super.onPostExecute(result);
         }
 
@@ -562,27 +662,16 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         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();
     }
 
+    // TODO: find a more robust way of handling image size selection
+    // for high screen densities.
     private int getScreenImageSize() {
-        DisplayMetrics metrics = new DisplayMetrics();
-        Display display = getWindowManager().getDefaultDisplay();
-        Point size = new Point();
-        display.getSize(size);
-        display.getMetrics(metrics);
-        int msize = Math.min(size.x, size.y);
-        return (133 * msize) / metrics.densityDpi;
+        DisplayMetrics outMetrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
+        return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
     }
 
     private void showSavingProgress(String albumName) {
@@ -649,7 +738,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         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());
+        mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
         intent.putExtra(Intent.EXTRA_STREAM, uri);
@@ -680,7 +769,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     @Override
     public void onPause() {
         super.onPause();
-        rsPause();
         if (mShareActionProvider != null) {
             mShareActionProvider.setOnShareTargetSelectedListener(null);
         }
@@ -689,72 +777,52 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     @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 = mImageLoader.getOriginalBitmapLarge();
-            FilteringPipeline pipeline = FilteringPipeline.getPipeline();
-            pipeline.setOriginal(largeBitmap);
-            float previewScale = (float) largeBitmap.getWidth() /
-                    (float) mImageLoader.getOriginalBounds().width();
-            pipeline.setPreviewScaleFactor(previewScale);
-            Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
-            if (highresBitmap != null) {
-                float highResPreviewScale = (float) highresBitmap.getWidth() /
-                        (float) mImageLoader.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()) {
             case R.id.undoButton: {
-                HistoryAdapter adapter = mMasterImage.getHistory();
+                HistoryManager adapter = mMasterImage.getHistory();
                 int position = adapter.undo();
                 mMasterImage.onHistoryItemClick(position);
                 backToMain();
                 invalidateViews();
+                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
+                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
                 return true;
             }
             case R.id.redoButton: {
-                HistoryAdapter adapter = mMasterImage.getHistory();
+                HistoryManager adapter = mMasterImage.getHistory();
                 int position = adapter.redo();
                 mMasterImage.onHistoryItemClick(position);
                 invalidateViews();
+                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
+                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
                 return true;
             }
             case R.id.resetHistoryButton: {
                 resetHistory();
+                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
+                        UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
                 return true;
             }
             case R.id.showImageStateButton: {
                 toggleImageStatePanel();
+                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
+                        UsageStatistics.CATEGORY_BUTTON_PRESS,
+                        mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
+                return true;
+            }
+            case R.id.exportFlattenButton: {
+                Uri sourceUri = MasterImage.getImage().getUri();
+                File dest = SaveImage.getNewFile(this, sourceUri);
+                Intent processIntent = ProcessingService.getSaveIntent(this, MasterImage.getImage()
+                        .getPreset(), dest, getSelectedImageUri(), sourceUri, true);
+                startService(processIntent);
                 return true;
             }
             case android.R.id.home: {
@@ -766,20 +834,18 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     }
 
     public void enableSave(boolean enable) {
-        if (mSaveButton != null)
+        if (mSaveButton != null) {
             mSaveButton.setEnabled(enable);
+        }
     }
 
-    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));
         }
@@ -787,10 +853,8 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
 
     public void setDefaultPreset() {
         // Default preset (original)
-        ImagePreset preset = new ImagePreset(getString(R.string.history_original)); // empty
-        preset.setImageLoader(mImageLoader);
-
-        mMasterImage.setPreset(preset, true);
+        ImagePreset preset = new ImagePreset(); // empty
+        mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
     }
 
     // //////////////////////////////////////////////////////////////////////////////
@@ -835,6 +899,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         super.onConfigurationChanged(newConfig);
         setDefaultValues();
         loadXML();
+        fillCategories();
         loadMainPanel();
 
         // mLoadBitmapTask==null implies you have looked at the intent
@@ -846,19 +911,14 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     }
 
     public void setupMasterImage() {
-        mImageLoader = new ImageLoader(this, getApplicationContext());
-
-        HistoryAdapter mHistoryAdapter = new HistoryAdapter(
-                this, R.layout.filtershow_history_operation_row,
-                R.id.rowTextView);
 
-        StateAdapter mImageStateAdapter = new StateAdapter(this, 0);
+        HistoryManager historyManager = new HistoryManager();
+        StateAdapter imageStateAdapter = new StateAdapter(this, 0);
         MasterImage.reset();
         mMasterImage = MasterImage.getImage();
-        mMasterImage.setHistoryAdapter(mHistoryAdapter);
-        mMasterImage.setStateAdapter(mImageStateAdapter);
+        mMasterImage.setHistoryManager(historyManager);
+        mMasterImage.setStateAdapter(imageStateAdapter);
         mMasterImage.setActivity(this);
-        mMasterImage.setImageLoader(mImageLoader);
 
         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
             mMasterImage.setSupportsHighRes(true);
@@ -868,10 +928,11 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
     }
 
     void resetHistory() {
-        HistoryAdapter adapter = mMasterImage.getHistory();
+        HistoryManager adapter = mMasterImage.getHistory();
         adapter.reset();
-        ImagePreset original = new ImagePreset(adapter.getItem(0));
-        mMasterImage.setPreset(original, true);
+        HistoryItem historyItem = adapter.getItem(0);
+        ImagePreset original = new ImagePreset(historyItem.getImagePreset());
+        mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
         invalidateViews();
         backToMain();
     }
@@ -902,11 +963,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();
                     }
@@ -960,7 +1023,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 = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
             showSavingProgress(albumName);
@@ -973,13 +1036,12 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
 
     public void done() {
         hideSavingProgress();
+        if (mLoadBitmapTask != null) {
+            mLoadBitmapTask.cancel(false);
+        }
         finish();
     }
 
-    static {
-        System.loadLibrary("jni_filtershow_filters");
-    }
-
     private void extractXMPData() {
         XMresults res = XmpPresets.extractXMPData(
                 getBaseContext(), mMasterImage, getIntent().getData());
@@ -989,4 +1051,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
         mOriginalImageUri = res.originalimage;
         mOriginalPreset = res.preset;
     }
+
+    public Uri getSelectedImageUri() {
+        return mSelectedImageUri;
+    }
+
 }