OSDN Git Service

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