OSDN Git Service

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