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