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