OSDN Git Service

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