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