OSDN Git Service

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