OSDN Git Service

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