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