OSDN Git Service

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