OSDN Git Service

Fix refresh of looks panel
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / FilterShowActivity.java
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.gallery3d.filtershow;
18
19 import android.app.ActionBar;
20 import android.app.AlertDialog;
21 import android.app.ProgressDialog;
22 import android.content.ComponentName;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.ServiceConnection;
28 import android.content.pm.ActivityInfo;
29 import android.content.res.Configuration;
30 import android.content.res.Resources;
31 import android.graphics.Bitmap;
32 import android.graphics.Rect;
33 import android.graphics.drawable.Drawable;
34 import android.net.Uri;
35 import android.os.AsyncTask;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.support.v4.app.DialogFragment;
40 import android.support.v4.app.Fragment;
41 import android.support.v4.app.FragmentActivity;
42 import android.support.v4.app.FragmentTransaction;
43 import android.util.DisplayMetrics;
44 import android.util.Log;
45 import android.util.TypedValue;
46 import android.view.Menu;
47 import android.view.MenuItem;
48 import android.view.MotionEvent;
49 import android.view.View;
50 import android.view.View.OnClickListener;
51 import android.view.ViewPropertyAnimator;
52 import android.view.WindowManager;
53 import android.widget.AdapterView;
54 import android.widget.AdapterView.OnItemClickListener;
55 import android.widget.FrameLayout;
56 import android.widget.ShareActionProvider;
57 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
58 import android.widget.Toast;
59
60 import com.android.gallery3d.R;
61 import com.android.gallery3d.app.PhotoPage;
62 import com.android.gallery3d.data.LocalAlbum;
63 import com.android.gallery3d.filtershow.cache.ImageLoader;
64 import com.android.gallery3d.filtershow.category.Action;
65 import com.android.gallery3d.filtershow.category.CategoryAdapter;
66 import com.android.gallery3d.filtershow.category.MainPanel;
67 import com.android.gallery3d.filtershow.category.SwipableView;
68 import com.android.gallery3d.filtershow.data.UserPresetsManager;
69 import com.android.gallery3d.filtershow.editors.BasicEditor;
70 import com.android.gallery3d.filtershow.editors.Editor;
71 import com.android.gallery3d.filtershow.editors.EditorChanSat;
72 import com.android.gallery3d.filtershow.editors.EditorCrop;
73 import com.android.gallery3d.filtershow.editors.EditorDraw;
74 import com.android.gallery3d.filtershow.editors.EditorGrad;
75 import com.android.gallery3d.filtershow.editors.EditorManager;
76 import com.android.gallery3d.filtershow.editors.EditorMirror;
77 import com.android.gallery3d.filtershow.editors.EditorPanel;
78 import com.android.gallery3d.filtershow.editors.EditorRedEye;
79 import com.android.gallery3d.filtershow.editors.EditorRotate;
80 import com.android.gallery3d.filtershow.editors.EditorStraighten;
81 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
82 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
83 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
84 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
85 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
86 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
87 import com.android.gallery3d.filtershow.filters.FiltersManager;
88 import com.android.gallery3d.filtershow.filters.ImageFilter;
89 import com.android.gallery3d.filtershow.history.HistoryItem;
90 import com.android.gallery3d.filtershow.history.HistoryManager;
91 import com.android.gallery3d.filtershow.imageshow.ImageShow;
92 import com.android.gallery3d.filtershow.imageshow.MasterImage;
93 import com.android.gallery3d.filtershow.imageshow.Spline;
94 import com.android.gallery3d.filtershow.info.InfoPanel;
95 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
96 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
97 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
98 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
99 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
100 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
101 import com.android.gallery3d.filtershow.state.StateAdapter;
102 import com.android.gallery3d.filtershow.tools.SaveImage;
103 import com.android.gallery3d.filtershow.tools.XmpPresets;
104 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
105 import com.android.gallery3d.filtershow.ui.ExportDialog;
106 import com.android.gallery3d.filtershow.ui.FramedTextButton;
107 import com.android.gallery3d.util.GalleryUtils;
108 import com.android.gallery3d.util.UsageStatistics;
109 import com.android.photos.data.GalleryBitmapPool;
110
111 import java.io.File;
112 import java.lang.ref.WeakReference;
113 import java.util.ArrayList;
114 import java.util.Vector;
115
116 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
117         OnShareTargetSelectedListener {
118
119     private String mAction = "";
120     MasterImage mMasterImage = null;
121
122     private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
123
124     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
125     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
126     private ImageShow mImageShow = null;
127
128     private View mSaveButton = null;
129
130     private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
131
132     private static final int SELECT_PICTURE = 1;
133     private static final String LOGTAG = "FilterShowActivity";
134
135     private boolean mShowingTinyPlanet = false;
136     private boolean mShowingImageStatePanel = false;
137     private boolean mShowingVersionsPanel = false;
138     private boolean mShowingInformationPanel = false;
139
140     private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
141
142     private ShareActionProvider mShareActionProvider;
143     private File mSharedOutputFile = null;
144
145     private boolean mSharingImage = false;
146
147     private WeakReference<ProgressDialog> mSavingProgressDialog;
148
149     private LoadBitmapTask mLoadBitmapTask;
150
151     private Uri mOriginalImageUri = null;
152     private ImagePreset mOriginalPreset = null;
153
154     private Uri mSelectedImageUri = null;
155
156     private UserPresetsManager mUserPresetsManager = null;
157     private UserPresetsAdapter mUserPresetsAdapter = null;
158     private CategoryAdapter mCategoryLooksAdapter = null;
159     private CategoryAdapter mCategoryBordersAdapter = null;
160     private CategoryAdapter mCategoryGeometryAdapter = null;
161     private CategoryAdapter mCategoryFiltersAdapter = null;
162     private CategoryAdapter mCategoryVersionsAdapter = null;
163     private int mCurrentPanel = MainPanel.LOOKS;
164     private Vector<FilterUserPresetRepresentation> mVersions =
165             new Vector<FilterUserPresetRepresentation>();
166     private int mVersionsCounter = 0;
167
168     private boolean mHandlingSwipeButton = false;
169     private View mHandledSwipeView = null;
170     private float mHandledSwipeViewLastDelta = 0;
171     private float mSwipeStartY = 0;
172
173     private ProcessingService mBoundService;
174     private boolean mIsBound = false;
175
176     public ProcessingService getProcessingService() {
177         return mBoundService;
178     }
179
180     public boolean isSimpleEditAction() {
181         return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
182     }
183
184     private ServiceConnection mConnection = new ServiceConnection() {
185         @Override
186         public void onServiceConnected(ComponentName className, IBinder service) {
187             /*
188              * This is called when the connection with the service has been
189              * established, giving us the service object we can use to
190              * interact with the service.  Because we have bound to a explicit
191              * service that we know is running in our own process, we can
192              * cast its IBinder to a concrete class and directly access it.
193              */
194             mBoundService = ((ProcessingService.LocalBinder)service).getService();
195             mBoundService.setFiltershowActivity(FilterShowActivity.this);
196             mBoundService.onStart();
197         }
198
199         @Override
200         public void onServiceDisconnected(ComponentName className) {
201             /*
202              * This is called when the connection with the service has been
203              * unexpectedly disconnected -- that is, its process crashed.
204              * Because it is running in our same process, we should never
205              * see this happen.
206              */
207             mBoundService = null;
208         }
209     };
210
211     void doBindService() {
212         /*
213          * Establish a connection with the service.  We use an explicit
214          * class name because we want a specific service implementation that
215          * we know will be running in our own process (and thus won't be
216          * supporting component replacement by other applications).
217          */
218         bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
219                 mConnection, Context.BIND_AUTO_CREATE);
220         mIsBound = true;
221     }
222
223     void doUnbindService() {
224         if (mIsBound) {
225             // Detach our existing connection.
226             unbindService(mConnection);
227             mIsBound = false;
228         }
229     }
230
231     private void setupPipeline() {
232         doBindService();
233         ImageFilter.setActivityForMemoryToasts(this);
234         mUserPresetsManager = new UserPresetsManager(this);
235         mUserPresetsAdapter = new UserPresetsAdapter(this);
236     }
237
238     public void updateUIAfterServiceStarted() {
239         fillCategories();
240         loadMainPanel();
241         setDefaultPreset();
242         extractXMPData();
243         processIntent();
244     }
245
246     @Override
247     public void onCreate(Bundle savedInstanceState) {
248         super.onCreate(savedInstanceState);
249
250         boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
251         if (onlyUsePortrait) {
252             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
253         }
254         MasterImage.setMaster(mMasterImage);
255
256         clearGalleryBitmapPool();
257         setupPipeline();
258
259         setupMasterImage();
260         setDefaultValues();
261         fillEditors();
262
263         loadXML();
264         UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
265         UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
266                 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
267     }
268
269     public boolean isShowingImageStatePanel() {
270         return mShowingImageStatePanel;
271     }
272
273     public void loadMainPanel() {
274         if (findViewById(R.id.main_panel_container) == null) {
275             return;
276         }
277         MainPanel panel = new MainPanel();
278         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
279         transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
280         transaction.commit();
281     }
282
283     public void loadEditorPanel(FilterRepresentation representation,
284                                 final Editor currentEditor) {
285         if (representation.getEditorId() == ImageOnlyEditor.ID) {
286             currentEditor.reflectCurrentFilter();
287             return;
288         }
289         final int currentId = currentEditor.getID();
290         Runnable showEditor = new Runnable() {
291             @Override
292             public void run() {
293                 EditorPanel panel = new EditorPanel();
294                 panel.setEditor(currentId);
295                 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
296                 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
297                 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
298                 transaction.commit();
299             }
300         };
301         Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
302         boolean doAnimation = false;
303         if (mShowingImageStatePanel
304                 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
305             doAnimation = true;
306         }
307         if (doAnimation && main != null && main instanceof MainPanel) {
308             MainPanel mainPanel = (MainPanel) main;
309             View container = mainPanel.getView().findViewById(R.id.category_panel_container);
310             View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
311             int panelHeight = container.getHeight() + bottom.getHeight();
312             ViewPropertyAnimator anim = mainPanel.getView().animate();
313             anim.translationY(panelHeight).start();
314             final Handler handler = new Handler();
315             handler.postDelayed(showEditor, anim.getDuration());
316         } else {
317             showEditor.run();
318         }
319     }
320
321     private void hideInformationPanel() {
322         FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
323         infoLayout.setVisibility(View.GONE);
324         Fragment fragment = getSupportFragmentManager().findFragmentByTag(InfoPanel.FRAGMENT_TAG);
325         if (fragment != null) {
326             FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
327             transaction.remove(fragment);
328             transaction.commit();
329         }
330         mShowingInformationPanel = false;
331     }
332
333     public void showInformationPanel() {
334         mShowingInformationPanel = !mShowingInformationPanel;
335         if (!mShowingInformationPanel) {
336             hideInformationPanel();
337             showDefaultImageView();
338             return;
339         }
340         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
341         transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
342         FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
343         infoLayout.setVisibility(View.VISIBLE);
344         mEditorPlaceHolder.hide();
345         mImageShow.setVisibility(View.GONE);
346
347         InfoPanel panel = new InfoPanel();
348         transaction.replace(R.id.central_panel_container, panel, InfoPanel.FRAGMENT_TAG);
349         transaction.commit();
350     }
351
352     private void loadXML() {
353         setContentView(R.layout.filtershow_activity);
354
355         ActionBar actionBar = getActionBar();
356         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
357         actionBar.setCustomView(R.layout.filtershow_actionbar);
358
359         mSaveButton = actionBar.getCustomView();
360         mSaveButton.setOnClickListener(new OnClickListener() {
361             @Override
362             public void onClick(View view) {
363                 saveImage();
364             }
365         });
366
367         mImageShow = (ImageShow) findViewById(R.id.imageShow);
368         mImageViews.add(mImageShow);
369
370         setupEditors();
371
372         mEditorPlaceHolder.hide();
373         mImageShow.bindAsImageLoadListener();
374
375         setupStatePanel();
376     }
377
378     public void fillCategories() {
379         fillLooks();
380         loadUserPresets();
381         fillBorders();
382         fillTools();
383         fillEffects();
384         fillVersions();
385     }
386
387     public void setupStatePanel() {
388         MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
389     }
390
391     private void fillVersions() {
392         mCategoryVersionsAdapter = new CategoryAdapter(this);
393         mCategoryVersionsAdapter.setShowAddButton(true);
394     }
395
396     public void updateVersions() {
397         mCategoryVersionsAdapter.clear();
398         FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
399                 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
400         mCategoryVersionsAdapter.add(
401                 new Action(this, originalRep, Action.FULL_VIEW));
402         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
403         FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
404                 getString(R.string.filtershow_version_current), current, -1);
405         mCategoryVersionsAdapter.add(
406                 new Action(this, currentRep, Action.FULL_VIEW));
407         if (mVersions.size() > 0) {
408             mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
409         }
410         for (FilterUserPresetRepresentation rep : mVersions) {
411             mCategoryVersionsAdapter.add(
412                     new Action(this, rep, Action.FULL_VIEW, true));
413         }
414         mCategoryVersionsAdapter.notifyDataSetInvalidated();
415     }
416
417     public void addCurrentVersion() {
418         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
419         mVersionsCounter++;
420         FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
421                 "" + mVersionsCounter, current, -1);
422         mVersions.add(rep);
423         updateVersions();
424     }
425
426     public void removeVersion(Action action) {
427         mVersions.remove(action.getRepresentation());
428         updateVersions();
429     }
430
431     public void removeLook(Action action) {
432         FilterUserPresetRepresentation rep =
433                 (FilterUserPresetRepresentation) action.getRepresentation();
434         if (rep == null) {
435             return;
436         }
437         mUserPresetsManager.delete(rep.getId());
438         updateUserPresetsFromManager();
439     }
440
441     private void fillEffects() {
442         FiltersManager filtersManager = FiltersManager.getManager();
443         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
444         mCategoryFiltersAdapter = new CategoryAdapter(this);
445         for (FilterRepresentation representation : filtersRepresentations) {
446             if (representation.getTextId() != 0) {
447                 representation.setName(getString(representation.getTextId()));
448             }
449             mCategoryFiltersAdapter.add(new Action(this, representation));
450         }
451     }
452
453     private void fillTools() {
454         FiltersManager filtersManager = FiltersManager.getManager();
455         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
456         mCategoryGeometryAdapter = new CategoryAdapter(this);
457         for (FilterRepresentation representation : filtersRepresentations) {
458             mCategoryGeometryAdapter.add(new Action(this, representation));
459         }
460     }
461
462     private void processIntent() {
463         Intent intent = getIntent();
464         if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
465             getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
466         }
467
468         mAction = intent.getAction();
469         mSelectedImageUri = intent.getData();
470         Uri loadUri = mSelectedImageUri;
471         if (mOriginalImageUri != null) {
472             loadUri = mOriginalImageUri;
473         }
474         if (loadUri != null) {
475             startLoadBitmap(loadUri);
476         } else {
477             pickImage();
478         }
479     }
480
481     private void setupEditors() {
482         mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
483         EditorManager.addEditors(mEditorPlaceHolder);
484         mEditorPlaceHolder.setOldViews(mImageViews);
485     }
486
487     private void fillEditors() {
488         mEditorPlaceHolder.addEditor(new EditorChanSat());
489         mEditorPlaceHolder.addEditor(new EditorGrad());
490         mEditorPlaceHolder.addEditor(new EditorDraw());
491         mEditorPlaceHolder.addEditor(new BasicEditor());
492         mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
493         mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
494         mEditorPlaceHolder.addEditor(new EditorRedEye());
495         mEditorPlaceHolder.addEditor(new EditorCrop());
496         mEditorPlaceHolder.addEditor(new EditorMirror());
497         mEditorPlaceHolder.addEditor(new EditorRotate());
498         mEditorPlaceHolder.addEditor(new EditorStraighten());
499     }
500
501     private void setDefaultValues() {
502         Resources res = getResources();
503
504         // TODO: get those values from XML.
505         FramedTextButton.setTextSize((int) getPixelsFromDip(14));
506         FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
507         FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
508
509         Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
510         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
511         Spline.setCurveHandle(curveHandle, curveHandleSize);
512         Spline.setCurveWidth((int) getPixelsFromDip(3));
513     }
514
515     private void startLoadBitmap(Uri uri) {
516         final View loading = findViewById(R.id.loading);
517         final View imageShow = findViewById(R.id.imageShow);
518         imageShow.setVisibility(View.INVISIBLE);
519         loading.setVisibility(View.VISIBLE);
520         mShowingTinyPlanet = false;
521         mLoadBitmapTask = new LoadBitmapTask();
522         mLoadBitmapTask.execute(uri);
523     }
524
525     private void fillBorders() {
526         FiltersManager filtersManager = FiltersManager.getManager();
527         ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
528
529         for (int i = 0; i < borders.size(); i++) {
530             FilterRepresentation filter = borders.get(i);
531             filter.setName(getString(R.string.borders));
532             if (i == 0) {
533                 filter.setName(getString(R.string.none));
534             }
535         }
536
537         mCategoryBordersAdapter = new CategoryAdapter(this);
538         for (FilterRepresentation representation : borders) {
539             if (representation.getTextId() != 0) {
540                 representation.setName(getString(representation.getTextId()));
541             }
542             mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
543         }
544     }
545
546     public UserPresetsAdapter getUserPresetsAdapter() {
547         return mUserPresetsAdapter;
548     }
549
550     public CategoryAdapter getCategoryLooksAdapter() {
551         return mCategoryLooksAdapter;
552     }
553
554     public CategoryAdapter getCategoryBordersAdapter() {
555         return mCategoryBordersAdapter;
556     }
557
558     public CategoryAdapter getCategoryGeometryAdapter() {
559         return mCategoryGeometryAdapter;
560     }
561
562     public CategoryAdapter getCategoryFiltersAdapter() {
563         return mCategoryFiltersAdapter;
564     }
565
566     public CategoryAdapter getCategoryVersionsAdapter() {
567         return mCategoryVersionsAdapter;
568     }
569
570     public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
571         if (filterRepresentation == null) {
572             return;
573         }
574         ImagePreset oldPreset = MasterImage.getImage().getPreset();
575         ImagePreset copy = new ImagePreset(oldPreset);
576         copy.removeFilter(filterRepresentation);
577         MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
578         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
579             FilterRepresentation lastRepresentation = copy.getLastRepresentation();
580             MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
581         }
582     }
583
584     public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
585         if (filterRepresentation == null) {
586             return;
587         }
588         if (!(filterRepresentation instanceof FilterRotateRepresentation)
589             && !(filterRepresentation instanceof FilterMirrorRepresentation)
590             && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
591             return;
592         }
593         ImagePreset oldPreset = MasterImage.getImage().getPreset();
594         ImagePreset copy = new ImagePreset(oldPreset);
595         FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
596         if (representation == null) {
597             copy.addFilter(filterRepresentation);
598         } else if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
599             representation.useParametersFrom(filterRepresentation);
600             filterRepresentation = representation;
601         } else {
602             if (filterRepresentation.allowsSingleInstanceOnly()) {
603                 // Don't just update the filter representation. Centralize the
604                 // logic in the addFilter(), such that we can keep "None" as
605                 // null.
606                 copy.removeFilter(representation);
607                 copy.addFilter(filterRepresentation);
608             }
609         }
610         MasterImage.getImage().setPreset(copy, filterRepresentation, true);
611         MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
612     }
613
614     public void showRepresentation(FilterRepresentation representation) {
615         if (representation == null) {
616             return;
617         }
618
619         if (representation instanceof FilterRotateRepresentation) {
620             FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
621             r.rotateCW();
622         }
623         if (representation instanceof FilterMirrorRepresentation) {
624             FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
625             r.cycle();
626         }
627         useFilterRepresentation(representation);
628
629         // show representation
630         Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
631         loadEditorPanel(representation, mCurrentEditor);
632         hideInformationPanel();
633     }
634
635     public Editor getEditor(int editorID) {
636         return mEditorPlaceHolder.getEditor(editorID);
637     }
638
639     public void setCurrentPanel(int currentPanel) {
640         mCurrentPanel = currentPanel;
641     }
642
643     public int getCurrentPanel() {
644         return mCurrentPanel;
645     }
646
647     public void updateCategories() {
648         ImagePreset preset = mMasterImage.getPreset();
649         mCategoryLooksAdapter.reflectImagePreset(preset);
650         mCategoryBordersAdapter.reflectImagePreset(preset);
651     }
652
653     public View getMainStatePanelContainer(int id) {
654         return findViewById(id);
655     }
656
657     private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
658         @Override
659         protected Boolean doInBackground(Void... params) {
660             MasterImage master = MasterImage.getImage();
661             Rect originalBounds = master.getOriginalBounds();
662             if (master.supportsHighRes()) {
663                 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
664                 if (highresPreviewSize > originalBounds.width()) {
665                     highresPreviewSize = originalBounds.width();
666                 }
667                 Rect bounds = new Rect();
668                 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
669                         master.getActivity(), highresPreviewSize,
670                         master.getOrientation(), bounds);
671                 master.setOriginalBounds(bounds);
672                 master.setOriginalBitmapHighres(originalHires);
673                 mBoundService.setOriginalBitmapHighres(originalHires);
674                 master.warnListeners();
675             }
676             return true;
677         }
678
679         @Override
680         protected void onPostExecute(Boolean result) {
681             Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
682             if (highresBitmap != null) {
683                 float highResPreviewScale = (float) highresBitmap.getWidth()
684                         / (float) MasterImage.getImage().getOriginalBounds().width();
685                 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
686             }
687         }
688     }
689
690     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
691         int mBitmapSize;
692
693         public LoadBitmapTask() {
694             mBitmapSize = getScreenImageSize();
695         }
696
697         @Override
698         protected Boolean doInBackground(Uri... params) {
699             if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
700                 return false;
701             }
702             publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
703             return true;
704         }
705
706         @Override
707         protected void onProgressUpdate(Boolean... values) {
708             super.onProgressUpdate(values);
709             if (isCancelled()) {
710                 return;
711             }
712             if (values[0]) {
713                 mShowingTinyPlanet = true;
714             }
715         }
716
717         @Override
718         protected void onPostExecute(Boolean result) {
719             MasterImage.setMaster(mMasterImage);
720             if (isCancelled()) {
721                 return;
722             }
723
724             if (!result) {
725                 cannotLoadImage();
726                 // TODO: We should figure out the best way preventing this from
727                 // happening, e.g: early checking.
728                 return;
729             }
730
731             if (null == CachingPipeline.getRenderScriptContext()){
732                 Log.v(LOGTAG,"RenderScript context destroyed during load");
733                 return;
734             }
735             final View loading = findViewById(R.id.loading);
736             loading.setVisibility(View.GONE);
737             final View imageShow = findViewById(R.id.imageShow);
738             imageShow.setVisibility(View.VISIBLE);
739
740             Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
741             mBoundService.setOriginalBitmap(largeBitmap);
742             MasterImage.getImage().resetGeometryImages();
743
744             float previewScale = (float) largeBitmap.getWidth()
745                     / (float) MasterImage.getImage().getOriginalBounds().width();
746             mBoundService.setPreviewScaleFactor(previewScale);
747             if (!mShowingTinyPlanet) {
748                 mCategoryFiltersAdapter.removeTinyPlanet();
749             }
750             mCategoryLooksAdapter.imageLoaded();
751             mCategoryBordersAdapter.imageLoaded();
752             mCategoryGeometryAdapter.imageLoaded();
753             mCategoryFiltersAdapter.imageLoaded();
754             mLoadBitmapTask = null;
755
756             if (mOriginalPreset != null) {
757                 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
758                 MasterImage.getImage().setPreset(mOriginalPreset,
759                         mOriginalPreset.getLastRepresentation(), true);
760                 mOriginalPreset = null;
761             }
762
763             if (mAction == TINY_PLANET_ACTION) {
764                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
765             }
766             LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
767             highresLoad.execute();
768             super.onPostExecute(result);
769         }
770
771     }
772
773     private void clearGalleryBitmapPool() {
774         (new AsyncTask<Void, Void, Void>() {
775             @Override
776             protected Void doInBackground(Void... params) {
777                 // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
778                 GalleryBitmapPool.getInstance().clear();
779                 return null;
780             }
781         }).execute();
782     }
783
784     @Override
785     protected void onDestroy() {
786         if (mLoadBitmapTask != null) {
787             mLoadBitmapTask.cancel(false);
788         }
789         mUserPresetsManager.close();
790         doUnbindService();
791         super.onDestroy();
792     }
793
794     // TODO: find a more robust way of handling image size selection
795     // for high screen densities.
796     private int getScreenImageSize() {
797         DisplayMetrics outMetrics = new DisplayMetrics();
798         getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
799         return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
800     }
801
802     private void showSavingProgress(String albumName) {
803         ProgressDialog progress;
804         if (mSavingProgressDialog != null) {
805             progress = mSavingProgressDialog.get();
806             if (progress != null) {
807                 progress.show();
808                 return;
809             }
810         }
811         // TODO: Allow cancellation of the saving process
812         String progressText;
813         if (albumName == null) {
814             progressText = getString(R.string.saving_image);
815         } else {
816             progressText = getString(R.string.filtershow_saving_image, albumName);
817         }
818         progress = ProgressDialog.show(this, "", progressText, true, false);
819         mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
820     }
821
822     private void hideSavingProgress() {
823         if (mSavingProgressDialog != null) {
824             ProgressDialog progress = mSavingProgressDialog.get();
825             if (progress != null)
826                 progress.dismiss();
827         }
828     }
829
830     public void completeSaveImage(Uri saveUri) {
831         if (mSharingImage && mSharedOutputFile != null) {
832             // Image saved, we unblock the content provider
833             Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
834                     Uri.encode(mSharedOutputFile.getAbsolutePath()));
835             ContentValues values = new ContentValues();
836             values.put(SharedImageProvider.PREPARE, false);
837             getContentResolver().insert(uri, values);
838         }
839         setResult(RESULT_OK, new Intent().setData(saveUri));
840         hideSavingProgress();
841         finish();
842     }
843
844     @Override
845     public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
846         // First, let's tell the SharedImageProvider that it will need to wait
847         // for the image
848         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
849                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
850         ContentValues values = new ContentValues();
851         values.put(SharedImageProvider.PREPARE, true);
852         getContentResolver().insert(uri, values);
853         mSharingImage = true;
854
855         // Process and save the image in the background.
856         showSavingProgress(null);
857         mImageShow.saveImage(this, mSharedOutputFile);
858         return true;
859     }
860
861     private Intent getDefaultShareIntent() {
862         Intent intent = new Intent(Intent.ACTION_SEND);
863         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
864         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
865         intent.setType(SharedImageProvider.MIME_TYPE);
866         mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
867         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
868                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
869         intent.putExtra(Intent.EXTRA_STREAM, uri);
870         return intent;
871     }
872
873     @Override
874     public boolean onCreateOptionsMenu(Menu menu) {
875         getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
876         MenuItem showState = menu.findItem(R.id.showImageStateButton);
877         if (mShowingImageStatePanel) {
878             showState.setTitle(R.string.hide_imagestate_panel);
879         } else {
880             showState.setTitle(R.string.show_imagestate_panel);
881         }
882         mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
883                 .getActionProvider();
884         mShareActionProvider.setShareIntent(getDefaultShareIntent());
885         mShareActionProvider.setOnShareTargetSelectedListener(this);
886
887         MenuItem undoItem = menu.findItem(R.id.undoButton);
888         MenuItem redoItem = menu.findItem(R.id.redoButton);
889         MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
890         mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
891         return true;
892     }
893
894     @Override
895     public void onPause() {
896         super.onPause();
897         if (mShareActionProvider != null) {
898             mShareActionProvider.setOnShareTargetSelectedListener(null);
899         }
900     }
901
902     @Override
903     public void onResume() {
904         super.onResume();
905         if (mShareActionProvider != null) {
906             mShareActionProvider.setOnShareTargetSelectedListener(this);
907         }
908     }
909
910     @Override
911     public boolean onOptionsItemSelected(MenuItem item) {
912         switch (item.getItemId()) {
913             case R.id.undoButton: {
914                 HistoryManager adapter = mMasterImage.getHistory();
915                 int position = adapter.undo();
916                 mMasterImage.onHistoryItemClick(position);
917                 backToMain();
918                 invalidateViews();
919                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
920                         UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
921                 return true;
922             }
923             case R.id.redoButton: {
924                 HistoryManager adapter = mMasterImage.getHistory();
925                 int position = adapter.redo();
926                 mMasterImage.onHistoryItemClick(position);
927                 invalidateViews();
928                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
929                         UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
930                 return true;
931             }
932             case R.id.resetHistoryButton: {
933                 resetHistory();
934                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
935                         UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
936                 return true;
937             }
938             case R.id.showImageStateButton: {
939                 toggleImageStatePanel();
940                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
941                         UsageStatistics.CATEGORY_BUTTON_PRESS,
942                         mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
943                 return true;
944             }
945             case R.id.exportFlattenButton: {
946                 showExportOptionsDialog();
947                 return true;
948             }
949             case android.R.id.home: {
950                 saveImage();
951                 return true;
952             }
953             case R.id.manageUserPresets: {
954                 manageUserPresets();
955                 return true;
956             }
957             case R.id.showInfoPanel: {
958                 showInformationPanel();
959                 return true;
960             }
961         }
962         return false;
963     }
964
965     public void addNewPreset() {
966         DialogFragment dialog = new PresetManagementDialog();
967         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
968     }
969
970     private void manageUserPresets() {
971         DialogFragment dialog = new PresetManagementDialog();
972         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
973     }
974
975     private void showExportOptionsDialog() {
976         DialogFragment dialog = new ExportDialog();
977         dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
978     }
979
980     public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
981         ArrayList<FilterUserPresetRepresentation> representations =
982                 adapter.getDeletedRepresentations();
983         for (FilterUserPresetRepresentation representation : representations) {
984             deletePreset(representation.getId());
985         }
986         ArrayList<FilterUserPresetRepresentation> changedRepresentations =
987                 adapter.getChangedRepresentations();
988         for (FilterUserPresetRepresentation representation : changedRepresentations) {
989             updatePreset(representation);
990         }
991         adapter.clearDeletedRepresentations();
992         adapter.clearChangedRepresentations();
993         loadUserPresets();
994     }
995
996     public void loadUserPresets() {
997         mUserPresetsManager.load();
998         updateUserPresetsFromManager();
999     }
1000
1001     public void updateUserPresetsFromManager() {
1002         ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1003         if (presets == null) {
1004             return;
1005         }
1006         if (mCategoryLooksAdapter != null) {
1007             fillLooks();
1008         }
1009         if (presets.size() > 0) {
1010             mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1011         }
1012         mUserPresetsAdapter.clear();
1013         for (int i = 0; i < presets.size(); i++) {
1014             FilterUserPresetRepresentation representation = presets.get(i);
1015             mCategoryLooksAdapter.add(
1016                     new Action(this, representation, Action.FULL_VIEW, true));
1017             mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1018         }
1019         if (presets.size() > 0) {
1020             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1021         }
1022         mCategoryLooksAdapter.notifyDataSetChanged();
1023         mCategoryLooksAdapter.notifyDataSetInvalidated();
1024     }
1025
1026     public void saveCurrentImagePreset(String name) {
1027         mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1028     }
1029
1030     private void deletePreset(int id) {
1031         mUserPresetsManager.delete(id);
1032     }
1033
1034     private void updatePreset(FilterUserPresetRepresentation representation) {
1035         mUserPresetsManager.update(representation);
1036     }
1037
1038     public void enableSave(boolean enable) {
1039         if (mSaveButton != null) {
1040             mSaveButton.setEnabled(enable);
1041         }
1042     }
1043
1044     private void fillLooks() {
1045         FiltersManager filtersManager = FiltersManager.getManager();
1046         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1047
1048         mCategoryLooksAdapter = new CategoryAdapter(this);
1049         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1050         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1051         for (FilterRepresentation representation : filtersRepresentations) {
1052             mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1053         }
1054         if (mUserPresetsManager.getRepresentations() == null
1055             || mUserPresetsManager.getRepresentations().size() == 0) {
1056             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1057         }
1058
1059         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1060         if (panel != null) {
1061             if (panel instanceof MainPanel) {
1062                 MainPanel mainPanel = (MainPanel) panel;
1063                 mainPanel.loadCategoryLookPanel(true);
1064             }
1065         }
1066     }
1067
1068     public void setDefaultPreset() {
1069         // Default preset (original)
1070         ImagePreset preset = new ImagePreset(); // empty
1071         mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1072     }
1073
1074     // //////////////////////////////////////////////////////////////////////////////
1075     // Some utility functions
1076     // TODO: finish the cleanup.
1077
1078     public void invalidateViews() {
1079         for (ImageShow views : mImageViews) {
1080             views.updateImage();
1081         }
1082     }
1083
1084     public void hideImageViews() {
1085         for (View view : mImageViews) {
1086             view.setVisibility(View.GONE);
1087         }
1088         mEditorPlaceHolder.hide();
1089     }
1090
1091     // //////////////////////////////////////////////////////////////////////////////
1092     // imageState panel...
1093
1094     public void toggleImageStatePanel() {
1095         invalidateOptionsMenu();
1096         mShowingImageStatePanel = !mShowingImageStatePanel;
1097         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1098         if (panel != null) {
1099             if (panel instanceof EditorPanel) {
1100                 EditorPanel editorPanel = (EditorPanel) panel;
1101                 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1102             } else if (panel instanceof MainPanel) {
1103                 MainPanel mainPanel = (MainPanel) panel;
1104                 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1105             }
1106         }
1107     }
1108
1109     public void toggleVersionsPanel() {
1110         mShowingVersionsPanel = !mShowingVersionsPanel;
1111         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1112         if (panel != null && panel instanceof MainPanel) {
1113             MainPanel mainPanel = (MainPanel) panel;
1114             mainPanel.loadCategoryVersionsPanel();
1115         }
1116     }
1117
1118     @Override
1119     public void onConfigurationChanged(Configuration newConfig)
1120     {
1121         super.onConfigurationChanged(newConfig);
1122         setDefaultValues();
1123         loadXML();
1124         fillCategories();
1125         loadMainPanel();
1126
1127         // mLoadBitmapTask==null implies you have looked at the intent
1128         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1129             mCategoryFiltersAdapter.removeTinyPlanet();
1130         }
1131         final View loading = findViewById(R.id.loading);
1132         loading.setVisibility(View.GONE);
1133     }
1134
1135     public void setupMasterImage() {
1136
1137         HistoryManager historyManager = new HistoryManager();
1138         StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1139         MasterImage.reset();
1140         mMasterImage = MasterImage.getImage();
1141         mMasterImage.setHistoryManager(historyManager);
1142         mMasterImage.setStateAdapter(imageStateAdapter);
1143         mMasterImage.setActivity(this);
1144
1145         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1146             mMasterImage.setSupportsHighRes(true);
1147         } else {
1148             mMasterImage.setSupportsHighRes(false);
1149         }
1150     }
1151
1152     void resetHistory() {
1153         HistoryManager adapter = mMasterImage.getHistory();
1154         adapter.reset();
1155         HistoryItem historyItem = adapter.getItem(0);
1156         ImagePreset original = new ImagePreset(historyItem.getImagePreset());
1157         mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
1158         invalidateViews();
1159         backToMain();
1160     }
1161
1162     public void showDefaultImageView() {
1163         hideInformationPanel();
1164         mEditorPlaceHolder.hide();
1165         mImageShow.setVisibility(View.VISIBLE);
1166         MasterImage.getImage().setCurrentFilter(null);
1167         MasterImage.getImage().setCurrentFilterRepresentation(null);
1168     }
1169
1170     public void backToMain() {
1171         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1172         if (currentPanel instanceof MainPanel) {
1173             return;
1174         }
1175         loadMainPanel();
1176         showDefaultImageView();
1177     }
1178
1179     @Override
1180     public void onBackPressed() {
1181         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1182         if (currentPanel instanceof MainPanel) {
1183             if (!mImageShow.hasModifications()) {
1184                 done();
1185             } else {
1186                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1187                 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1188                 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1189                     @Override
1190                     public void onClick(DialogInterface dialog, int id) {
1191                         saveImage();
1192                     }
1193                 });
1194                 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1195                     @Override
1196                     public void onClick(DialogInterface dialog, int id) {
1197                         done();
1198                     }
1199                 });
1200                 builder.show();
1201             }
1202         } else {
1203             backToMain();
1204         }
1205     }
1206
1207     public void cannotLoadImage() {
1208         Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1209         finish();
1210     }
1211
1212     // //////////////////////////////////////////////////////////////////////////////
1213
1214     public float getPixelsFromDip(float value) {
1215         Resources r = getResources();
1216         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1217                 r.getDisplayMetrics());
1218     }
1219
1220     @Override
1221     public void onItemClick(AdapterView<?> parent, View view, int position,
1222             long id) {
1223         mMasterImage.onHistoryItemClick(position);
1224         invalidateViews();
1225     }
1226
1227     public void pickImage() {
1228         Intent intent = new Intent();
1229         intent.setType("image/*");
1230         intent.setAction(Intent.ACTION_GET_CONTENT);
1231         startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1232                 SELECT_PICTURE);
1233     }
1234
1235     @Override
1236     public void onActivityResult(int requestCode, int resultCode, Intent data) {
1237         if (resultCode == RESULT_OK) {
1238             if (requestCode == SELECT_PICTURE) {
1239                 Uri selectedImageUri = data.getData();
1240                 startLoadBitmap(selectedImageUri);
1241             }
1242         }
1243     }
1244
1245
1246     public void saveImage() {
1247         if (mImageShow.hasModifications()) {
1248             // Get the name of the album, to which the image will be saved
1249             File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1250             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1251             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1252             showSavingProgress(albumName);
1253             mImageShow.saveImage(this, null);
1254         } else {
1255             done();
1256         }
1257     }
1258
1259
1260     public void done() {
1261         hideSavingProgress();
1262         if (mLoadBitmapTask != null) {
1263             mLoadBitmapTask.cancel(false);
1264         }
1265         finish();
1266     }
1267
1268     private void extractXMPData() {
1269         XMresults res = XmpPresets.extractXMPData(
1270                 getBaseContext(), mMasterImage, getIntent().getData());
1271         if (res == null)
1272             return;
1273
1274         mOriginalImageUri = res.originalimage;
1275         mOriginalPreset = res.preset;
1276     }
1277
1278     public Uri getSelectedImageUri() {
1279         return mSelectedImageUri;
1280     }
1281
1282     public void setHandlesSwipeForView(View view, float startY) {
1283         if (view != null) {
1284             mHandlingSwipeButton = true;
1285         } else {
1286             mHandlingSwipeButton = false;
1287         }
1288         mHandledSwipeView = view;
1289         int[] location = new int[2];
1290         view.getLocationInWindow(location);
1291         mSwipeStartY = location[1] + startY;
1292     }
1293
1294     public boolean dispatchTouchEvent (MotionEvent ev) {
1295         if (mHandlingSwipeButton) {
1296             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1297                 float delta = ev.getY() - mSwipeStartY;
1298                 mHandledSwipeView.setTranslationY(delta);
1299                 delta = Math.abs(delta);
1300                 float transparency = Math.min(1, delta / mHandledSwipeView.getHeight());
1301                 mHandledSwipeView.setAlpha(1.f - transparency);
1302                 mHandledSwipeViewLastDelta = delta;
1303             }
1304             if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1305                     || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1306                 mHandledSwipeView.setTranslationY(0);
1307                 mHandledSwipeView.setAlpha(1.f);
1308                 mHandlingSwipeButton = false;
1309                 if (mHandledSwipeViewLastDelta > mHandledSwipeView.getHeight()) {
1310                     ((SwipableView) mHandledSwipeView).delete();
1311                 }
1312             }
1313             return true;
1314         }
1315         return super.dispatchTouchEvent(ev);
1316     }
1317 }