FilterShowActivity.java revision 6a50273a45cdda757daf9840e9e36a2b60eff1aa
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.ContentValues;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.content.pm.ActivityInfo;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.graphics.Bitmap;
29import android.graphics.Point;
30import android.graphics.drawable.Drawable;
31import android.net.Uri;
32import android.os.AsyncTask;
33import android.os.Bundle;
34import android.os.Handler;
35import android.support.v4.app.Fragment;
36import android.support.v4.app.FragmentActivity;
37import android.support.v4.app.FragmentTransaction;
38import android.util.DisplayMetrics;
39import android.util.Log;
40import android.util.TypedValue;
41import android.view.Display;
42import android.view.Menu;
43import android.view.MenuItem;
44import android.view.View;
45import android.view.View.OnClickListener;
46import android.view.ViewPropertyAnimator;
47import android.view.WindowManager;
48import android.widget.AdapterView;
49import android.widget.AdapterView.OnItemClickListener;
50import android.widget.FrameLayout;
51import android.widget.ShareActionProvider;
52import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
53import android.widget.Toast;
54
55import com.android.gallery3d.R;
56import com.android.gallery3d.data.LocalAlbum;
57import com.android.gallery3d.filtershow.cache.CachingPipeline;
58import com.android.gallery3d.filtershow.cache.FilteringPipeline;
59import com.android.gallery3d.filtershow.cache.ImageLoader;
60import com.android.gallery3d.filtershow.category.Action;
61import com.android.gallery3d.filtershow.category.CategoryAdapter;
62import com.android.gallery3d.filtershow.category.CategoryView;
63import com.android.gallery3d.filtershow.category.MainPanel;
64import com.android.gallery3d.filtershow.editors.BasicEditor;
65import com.android.gallery3d.filtershow.editors.Editor;
66import com.android.gallery3d.filtershow.editors.EditorCrop;
67import com.android.gallery3d.filtershow.editors.EditorDraw;
68import com.android.gallery3d.filtershow.editors.EditorFlip;
69import com.android.gallery3d.filtershow.editors.EditorInfo;
70import com.android.gallery3d.filtershow.editors.EditorManager;
71import com.android.gallery3d.filtershow.editors.EditorPanel;
72import com.android.gallery3d.filtershow.editors.EditorRedEye;
73import com.android.gallery3d.filtershow.editors.EditorRotate;
74import com.android.gallery3d.filtershow.editors.EditorStraighten;
75import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
76import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
77import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
78import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
79import com.android.gallery3d.filtershow.filters.FilterRepresentation;
80import com.android.gallery3d.filtershow.filters.FiltersManager;
81import com.android.gallery3d.filtershow.filters.ImageFilter;
82import com.android.gallery3d.filtershow.history.HistoryManager;
83import com.android.gallery3d.filtershow.history.HistoryItem;
84import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
85import com.android.gallery3d.filtershow.imageshow.ImageCrop;
86import com.android.gallery3d.filtershow.imageshow.ImageShow;
87import com.android.gallery3d.filtershow.imageshow.MasterImage;
88import com.android.gallery3d.filtershow.presets.ImagePreset;
89import com.android.gallery3d.filtershow.provider.SharedImageProvider;
90import com.android.gallery3d.filtershow.state.StateAdapter;
91import com.android.gallery3d.filtershow.tools.SaveCopyTask;
92import com.android.gallery3d.filtershow.tools.XmpPresets;
93import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
94import com.android.gallery3d.filtershow.ui.FramedTextButton;
95import com.android.gallery3d.filtershow.ui.Spline;
96import com.android.gallery3d.util.GalleryUtils;
97import com.android.gallery3d.util.UsageStatistics;
98import com.android.photos.data.GalleryBitmapPool;
99
100import java.io.File;
101import java.lang.ref.WeakReference;
102import java.util.Vector;
103
104public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
105        OnShareTargetSelectedListener {
106
107    private String mAction = "";
108    MasterImage mMasterImage = null;
109
110    private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
111
112    public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
113    public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
114    private ImageLoader mImageLoader = null;
115    private ImageShow mImageShow = null;
116
117    private View mSaveButton = null;
118
119    private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
120
121    private static final int SELECT_PICTURE = 1;
122    private static final String LOGTAG = "FilterShowActivity";
123    protected static final boolean ANIMATE_PANELS = true;
124
125    private boolean mShowingTinyPlanet = false;
126    private boolean mShowingImageStatePanel = false;
127
128    private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
129
130    private ShareActionProvider mShareActionProvider;
131    private File mSharedOutputFile = null;
132
133    private boolean mSharingImage = false;
134
135    private WeakReference<ProgressDialog> mSavingProgressDialog;
136
137    private LoadBitmapTask mLoadBitmapTask;
138    private boolean mLoading = true;
139
140    private Uri mOriginalImageUri = null;
141    private ImagePreset mOriginalPreset = null;
142
143    private Uri mSelectedImageUri = null;
144
145    private CategoryAdapter mCategoryLooksAdapter = null;
146    private CategoryAdapter mCategoryBordersAdapter = null;
147    private CategoryAdapter mCategoryGeometryAdapter = null;
148    private CategoryAdapter mCategoryFiltersAdapter = null;
149    private int mCurrentPanel = MainPanel.LOOKS;
150
151    @Override
152    public void onCreate(Bundle savedInstanceState) {
153        super.onCreate(savedInstanceState);
154
155        boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
156        if (onlyUsePortrait) {
157            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
158        }
159        MasterImage.setMaster(mMasterImage);
160
161        clearGalleryBitmapPool();
162
163        CachingPipeline.createRenderscriptContext(this);
164        setupMasterImage();
165        setDefaultValues();
166        fillEditors();
167
168        loadXML();
169        loadMainPanel();
170
171        setDefaultPreset();
172
173        extractXMPData();
174        processIntent();
175        UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
176        UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
177                UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
178    }
179
180    public boolean isShowingImageStatePanel() {
181        return mShowingImageStatePanel;
182    }
183
184    public void loadMainPanel() {
185        if (findViewById(R.id.main_panel_container) == null) {
186            return;
187        }
188        MainPanel panel = new MainPanel();
189        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
190        transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
191        transaction.commit();
192    }
193
194    public void loadEditorPanel(FilterRepresentation representation,
195                                final Editor currentEditor) {
196        if (representation.getEditorId() == ImageOnlyEditor.ID) {
197            currentEditor.reflectCurrentFilter();
198            return;
199        }
200        final int currentId = currentEditor.getID();
201        Runnable showEditor = new Runnable() {
202            @Override
203            public void run() {
204                EditorPanel panel = new EditorPanel();
205                panel.setEditor(currentId);
206                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
207                transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
208                transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
209                transaction.commit();
210            }
211        };
212        Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
213        boolean doAnimation = false;
214        if (mShowingImageStatePanel
215                && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
216            doAnimation = true;
217        }
218        if (doAnimation && main != null && main instanceof MainPanel) {
219            MainPanel mainPanel = (MainPanel) main;
220            View container = mainPanel.getView().findViewById(R.id.category_panel_container);
221            View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
222            int panelHeight = container.getHeight() + bottom.getHeight();
223            ViewPropertyAnimator anim = mainPanel.getView().animate();
224            anim.translationY(panelHeight).start();
225            final Handler handler = new Handler();
226            handler.postDelayed(showEditor, anim.getDuration());
227        } else {
228            showEditor.run();
229        }
230    }
231
232    private void loadXML() {
233        setContentView(R.layout.filtershow_activity);
234
235        ActionBar actionBar = getActionBar();
236        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
237        actionBar.setCustomView(R.layout.filtershow_actionbar);
238
239        mSaveButton = actionBar.getCustomView();
240        mSaveButton.setOnClickListener(new OnClickListener() {
241            @Override
242            public void onClick(View view) {
243                saveImage();
244            }
245        });
246
247        mImageShow = (ImageShow) findViewById(R.id.imageShow);
248        mImageViews.add(mImageShow);
249
250        setupEditors();
251
252        mEditorPlaceHolder.hide();
253
254        mImageShow.setImageLoader(mImageLoader);
255
256        fillFx();
257        fillBorders();
258        fillGeometry();
259        fillFilters();
260
261        setupStatePanel();
262    }
263
264    public void setupStatePanel() {
265        mImageLoader.setHistoryManager(mMasterImage.getHistory());
266    }
267
268    private void fillFilters() {
269        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
270        FiltersManager filtersManager = FiltersManager.getManager();
271        filtersManager.addEffects(filtersRepresentations);
272
273        mCategoryFiltersAdapter = new CategoryAdapter(this);
274        for (FilterRepresentation representation : filtersRepresentations) {
275            if (representation.getTextId() != 0) {
276                representation.setName(getString(representation.getTextId()));
277            }
278            mCategoryFiltersAdapter.add(new Action(this, representation));
279        }
280    }
281
282    private void fillGeometry() {
283        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
284        FiltersManager filtersManager = FiltersManager.getManager();
285
286        GeometryMetadata geo = new GeometryMetadata();
287        int[] editorsId = geo.getEditorIds();
288        for (int i = 0; i < editorsId.length; i++) {
289            int editorId = editorsId[i];
290            GeometryMetadata geometry = new GeometryMetadata(geo);
291            geometry.setEditorId(editorId);
292            EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId);
293            geometry.setTextId(editorInfo.getTextId());
294            geometry.setOverlayId(editorInfo.getOverlayId());
295            geometry.setOverlayOnly(editorInfo.getOverlayOnly());
296            if (geometry.getTextId() != 0) {
297                geometry.setName(getString(geometry.getTextId()));
298            }
299            filtersRepresentations.add(geometry);
300        }
301
302        filtersManager.addTools(filtersRepresentations);
303
304        mCategoryGeometryAdapter = new CategoryAdapter(this);
305        for (FilterRepresentation representation : filtersRepresentations) {
306            mCategoryGeometryAdapter.add(new Action(this, representation));
307        }
308    }
309
310    private void processIntent() {
311        Intent intent = getIntent();
312        if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
313            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
314        }
315
316        mAction = intent.getAction();
317        mSelectedImageUri = intent.getData();
318        Uri loadUri = mSelectedImageUri;
319        if (mOriginalImageUri != null) {
320            loadUri = mOriginalImageUri;
321        }
322        if (loadUri != null) {
323            startLoadBitmap(loadUri);
324        } else {
325            pickImage();
326        }
327    }
328
329    private void setupEditors() {
330        mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
331        EditorManager.addEditors(mEditorPlaceHolder);
332        mEditorPlaceHolder.setOldViews(mImageViews);
333        mEditorPlaceHolder.setImageLoader(mImageLoader);
334    }
335
336    private void fillEditors() {
337        mEditorPlaceHolder.addEditor(new EditorDraw());
338        mEditorPlaceHolder.addEditor(new BasicEditor());
339        mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
340        mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
341        mEditorPlaceHolder.addEditor(new EditorRedEye());
342        mEditorPlaceHolder.addEditor(new EditorCrop());
343        mEditorPlaceHolder.addEditor(new EditorFlip());
344        mEditorPlaceHolder.addEditor(new EditorRotate());
345        mEditorPlaceHolder.addEditor(new EditorStraighten());
346    }
347
348    private void setDefaultValues() {
349        ImageFilter.setActivityForMemoryToasts(this);
350
351        Resources res = getResources();
352        FiltersManager.setResources(res);
353
354        CategoryView.setMargin((int) getPixelsFromDip(8));
355        CategoryView.setTextSize((int) getPixelsFromDip(16));
356
357        // TODO: get those values from XML.
358        FramedTextButton.setTextSize((int) getPixelsFromDip(14));
359        FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
360        FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
361
362        Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
363        int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
364        Spline.setCurveHandle(curveHandle, curveHandleSize);
365        Spline.setCurveWidth((int) getPixelsFromDip(3));
366
367        ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
368        ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
369        ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
370    }
371
372    private void startLoadBitmap(Uri uri) {
373        mLoading = true;
374        final View loading = findViewById(R.id.loading);
375        final View imageShow = findViewById(R.id.imageShow);
376        imageShow.setVisibility(View.INVISIBLE);
377        loading.setVisibility(View.VISIBLE);
378        mShowingTinyPlanet = false;
379        mLoadBitmapTask = new LoadBitmapTask();
380        mLoadBitmapTask.execute(uri);
381    }
382
383    private void fillBorders() {
384        Vector<FilterRepresentation> borders = new Vector<FilterRepresentation>();
385
386        // The "no border" implementation
387        borders.add(new FilterImageBorderRepresentation(0));
388
389        // Google-build borders
390        FiltersManager.getManager().addBorders(this, borders);
391
392        for (int i = 0; i < borders.size(); i++) {
393            FilterRepresentation filter = borders.elementAt(i);
394            filter.setName(getString(R.string.borders));
395            if (i == 0) {
396                filter.setName(getString(R.string.none));
397            }
398        }
399
400        mCategoryBordersAdapter = new CategoryAdapter(this);
401        for (FilterRepresentation representation : borders) {
402            if (representation.getTextId() != 0) {
403                representation.setName(getString(representation.getTextId()));
404            }
405            mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
406        }
407    }
408
409    public CategoryAdapter getCategoryLooksAdapter() {
410        return mCategoryLooksAdapter;
411    }
412
413    public CategoryAdapter getCategoryBordersAdapter() {
414        return mCategoryBordersAdapter;
415    }
416
417    public CategoryAdapter getCategoryGeometryAdapter() {
418        return mCategoryGeometryAdapter;
419    }
420
421    public CategoryAdapter getCategoryFiltersAdapter() {
422        return mCategoryFiltersAdapter;
423    }
424
425    public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
426        if (filterRepresentation == null) {
427            return;
428        }
429        ImagePreset oldPreset = MasterImage.getImage().getPreset();
430        ImagePreset copy = new ImagePreset(oldPreset);
431        copy.removeFilter(filterRepresentation);
432        MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
433        if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
434            FilterRepresentation lastRepresentation = copy.getLastRepresentation();
435            MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
436        }
437    }
438
439    public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
440        if (filterRepresentation == null) {
441            return;
442        }
443        if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
444            return;
445        }
446        ImagePreset oldPreset = MasterImage.getImage().getPreset();
447        ImagePreset copy = new ImagePreset(oldPreset);
448        FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
449        if (representation == null) {
450            copy.addFilter(filterRepresentation);
451        } else {
452            if (filterRepresentation.allowsSingleInstanceOnly()) {
453                // Don't just update the filter representation. Centralize the
454                // logic in the addFilter(), such that we can keep "None" as
455                // null.
456                copy.removeFilter(representation);
457                copy.addFilter(filterRepresentation);
458            }
459        }
460        MasterImage.getImage().setPreset(copy, filterRepresentation, true);
461        MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
462    }
463
464    public void showRepresentation(FilterRepresentation representation) {
465        if (representation == null) {
466            return;
467        }
468
469        // TODO: this check is needed because the GeometryMetadata doesn't quite
470        // follow the same pattern as the other filters to update/sync their values.
471        // We thus need to not call useFilterRepresentation() for now, as it
472        // would override the current Geometry. Once GeometryMetadata is fixed,
473        // let's remove the check and call useFilterRepresentation all the time.
474        if (!(representation instanceof GeometryMetadata)) {
475            useFilterRepresentation(representation);
476        }
477
478        // show representation
479        Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
480        loadEditorPanel(representation, mCurrentEditor);
481    }
482
483    public Editor getEditor(int editorID) {
484        return mEditorPlaceHolder.getEditor(editorID);
485    }
486
487    public void setCurrentPanel(int currentPanel) {
488        mCurrentPanel = currentPanel;
489    }
490
491    public int getCurrentPanel() {
492        return mCurrentPanel;
493    }
494
495    public void updateCategories() {
496        ImagePreset preset = mMasterImage.getPreset();
497        mCategoryLooksAdapter.reflectImagePreset(preset);
498        mCategoryBordersAdapter.reflectImagePreset(preset);
499    }
500
501    private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
502        int mBitmapSize;
503
504        public LoadBitmapTask() {
505            mBitmapSize = getScreenImageSize();
506        }
507
508        @Override
509        protected Boolean doInBackground(Uri... params) {
510            if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
511                return false;
512            }
513            publishProgress(mImageLoader.queryLightCycle360());
514            return true;
515        }
516
517        @Override
518        protected void onProgressUpdate(Boolean... values) {
519            super.onProgressUpdate(values);
520            if (isCancelled()) {
521                return;
522            }
523            if (values[0]) {
524                mShowingTinyPlanet = true;
525            }
526        }
527
528        @Override
529        protected void onPostExecute(Boolean result) {
530            MasterImage.setMaster(mMasterImage);
531            if (isCancelled()) {
532                return;
533            }
534
535            if (!result) {
536                cannotLoadImage();
537            }
538
539            if (null == CachingPipeline.getRenderScriptContext()){
540                Log.v(LOGTAG,"RenderScript context destroyed during load");
541                return;
542            }
543            final View loading = findViewById(R.id.loading);
544            loading.setVisibility(View.GONE);
545            final View imageShow = findViewById(R.id.imageShow);
546            imageShow.setVisibility(View.VISIBLE);
547
548            Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
549            FilteringPipeline pipeline = FilteringPipeline.getPipeline();
550            pipeline.setOriginal(largeBitmap);
551            float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
552            pipeline.setPreviewScaleFactor(previewScale);
553            Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
554            if (highresBitmap != null) {
555                float highResPreviewScale = (float) highresBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
556                pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
557            }
558            if (!mShowingTinyPlanet) {
559                mCategoryFiltersAdapter.removeTinyPlanet();
560            }
561            pipeline.turnOnPipeline(true);
562            MasterImage.getImage().setOriginalGeometry(largeBitmap);
563            mCategoryLooksAdapter.imageLoaded();
564            mCategoryBordersAdapter.imageLoaded();
565            mCategoryGeometryAdapter.imageLoaded();
566            mCategoryFiltersAdapter.imageLoaded();
567            mLoadBitmapTask = null;
568
569            if (mOriginalPreset != null) {
570                MasterImage.getImage().setLoadedPreset(mOriginalPreset);
571                MasterImage.getImage().setPreset(mOriginalPreset,
572                        mOriginalPreset.getLastRepresentation(), true);
573                mOriginalPreset = null;
574            }
575
576            if (mAction == TINY_PLANET_ACTION) {
577                showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
578            }
579            mLoading = false;
580            MasterImage.getImage().notifyGeometryChange();
581            super.onPostExecute(result);
582        }
583
584    }
585
586    private void clearGalleryBitmapPool() {
587        (new AsyncTask<Void, Void, Void>() {
588            @Override
589            protected Void doInBackground(Void... params) {
590                // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
591                GalleryBitmapPool.getInstance().clear();
592                return null;
593            }
594        }).execute();
595    }
596
597    @Override
598    protected void onDestroy() {
599        if (mLoadBitmapTask != null) {
600            mLoadBitmapTask.cancel(false);
601        }
602        // TODO:  refactor, don't use so many singletons.
603        FilteringPipeline.getPipeline().turnOnPipeline(false);
604        MasterImage.reset();
605        FilteringPipeline.reset();
606        ImageFilter.resetStatics();
607        FiltersManager.getPreviewManager().freeRSFilterScripts();
608        FiltersManager.getManager().freeRSFilterScripts();
609        FiltersManager.getHighresManager().freeRSFilterScripts();
610        FiltersManager.reset();
611        CachingPipeline.destroyRenderScriptContext();
612        super.onDestroy();
613    }
614
615    private int getScreenImageSize() {
616        DisplayMetrics metrics = new DisplayMetrics();
617        Display display = getWindowManager().getDefaultDisplay();
618        Point size = new Point();
619        display.getSize(size);
620        display.getMetrics(metrics);
621        int msize = Math.min(size.x, size.y);
622        return (133 * msize) / metrics.densityDpi;
623    }
624
625    private void showSavingProgress(String albumName) {
626        ProgressDialog progress;
627        if (mSavingProgressDialog != null) {
628            progress = mSavingProgressDialog.get();
629            if (progress != null) {
630                progress.show();
631                return;
632            }
633        }
634        // TODO: Allow cancellation of the saving process
635        String progressText;
636        if (albumName == null) {
637            progressText = getString(R.string.saving_image);
638        } else {
639            progressText = getString(R.string.filtershow_saving_image, albumName);
640        }
641        progress = ProgressDialog.show(this, "", progressText, true, false);
642        mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
643    }
644
645    private void hideSavingProgress() {
646        if (mSavingProgressDialog != null) {
647            ProgressDialog progress = mSavingProgressDialog.get();
648            if (progress != null)
649                progress.dismiss();
650        }
651    }
652
653    public void completeSaveImage(Uri saveUri) {
654        if (mSharingImage && mSharedOutputFile != null) {
655            // Image saved, we unblock the content provider
656            Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
657                    Uri.encode(mSharedOutputFile.getAbsolutePath()));
658            ContentValues values = new ContentValues();
659            values.put(SharedImageProvider.PREPARE, false);
660            getContentResolver().insert(uri, values);
661        }
662        setResult(RESULT_OK, new Intent().setData(saveUri));
663        hideSavingProgress();
664        finish();
665    }
666
667    @Override
668    public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
669        // First, let's tell the SharedImageProvider that it will need to wait
670        // for the image
671        Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
672                Uri.encode(mSharedOutputFile.getAbsolutePath()));
673        ContentValues values = new ContentValues();
674        values.put(SharedImageProvider.PREPARE, true);
675        getContentResolver().insert(uri, values);
676        mSharingImage = true;
677
678        // Process and save the image in the background.
679        showSavingProgress(null);
680        mImageShow.saveImage(this, mSharedOutputFile);
681        return true;
682    }
683
684    private Intent getDefaultShareIntent() {
685        Intent intent = new Intent(Intent.ACTION_SEND);
686        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
687        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
688        intent.setType(SharedImageProvider.MIME_TYPE);
689        mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri());
690        Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
691                Uri.encode(mSharedOutputFile.getAbsolutePath()));
692        intent.putExtra(Intent.EXTRA_STREAM, uri);
693        return intent;
694    }
695
696    @Override
697    public boolean onCreateOptionsMenu(Menu menu) {
698        getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
699        MenuItem showState = menu.findItem(R.id.showImageStateButton);
700        if (mShowingImageStatePanel) {
701            showState.setTitle(R.string.hide_imagestate_panel);
702        } else {
703            showState.setTitle(R.string.show_imagestate_panel);
704        }
705        mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
706                .getActionProvider();
707        mShareActionProvider.setShareIntent(getDefaultShareIntent());
708        mShareActionProvider.setOnShareTargetSelectedListener(this);
709
710        MenuItem undoItem = menu.findItem(R.id.undoButton);
711        MenuItem redoItem = menu.findItem(R.id.redoButton);
712        MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
713        mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
714        return true;
715    }
716
717    @Override
718    public void onPause() {
719        super.onPause();
720        rsPause();
721        if (mShareActionProvider != null) {
722            mShareActionProvider.setOnShareTargetSelectedListener(null);
723        }
724    }
725
726    @Override
727    public void onResume() {
728        super.onResume();
729        rsResume();
730        if (mShareActionProvider != null) {
731            mShareActionProvider.setOnShareTargetSelectedListener(this);
732        }
733    }
734
735    private void rsResume() {
736        ImageFilter.setActivityForMemoryToasts(this);
737        MasterImage.setMaster(mMasterImage);
738        if (CachingPipeline.getRenderScriptContext() == null) {
739            CachingPipeline.createRenderscriptContext(this);
740        }
741        FiltersManager.setResources(getResources());
742        if (!mLoading) {
743            Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
744            FilteringPipeline pipeline = FilteringPipeline.getPipeline();
745            pipeline.setOriginal(largeBitmap);
746            float previewScale = (float) largeBitmap.getWidth() /
747                    (float) mImageLoader.getOriginalBounds().width();
748            pipeline.setPreviewScaleFactor(previewScale);
749            Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
750            if (highresBitmap != null) {
751                float highResPreviewScale = (float) highresBitmap.getWidth() /
752                        (float) mImageLoader.getOriginalBounds().width();
753                pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
754            }
755            pipeline.turnOnPipeline(true);
756            MasterImage.getImage().setOriginalGeometry(largeBitmap);
757        }
758    }
759
760    private void rsPause() {
761        FilteringPipeline.getPipeline().turnOnPipeline(false);
762        FilteringPipeline.reset();
763        ImageFilter.resetStatics();
764        FiltersManager.getPreviewManager().freeRSFilterScripts();
765        FiltersManager.getManager().freeRSFilterScripts();
766        FiltersManager.getHighresManager().freeRSFilterScripts();
767        FiltersManager.reset();
768        CachingPipeline.destroyRenderScriptContext();
769    }
770
771    @Override
772    public boolean onOptionsItemSelected(MenuItem item) {
773        switch (item.getItemId()) {
774            case R.id.undoButton: {
775                HistoryManager adapter = mMasterImage.getHistory();
776                int position = adapter.undo();
777                mMasterImage.onHistoryItemClick(position);
778                backToMain();
779                invalidateViews();
780                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
781                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
782                return true;
783            }
784            case R.id.redoButton: {
785                HistoryManager adapter = mMasterImage.getHistory();
786                int position = adapter.redo();
787                mMasterImage.onHistoryItemClick(position);
788                invalidateViews();
789                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
790                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
791                return true;
792            }
793            case R.id.resetHistoryButton: {
794                resetHistory();
795                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
796                        UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
797                return true;
798            }
799            case R.id.showImageStateButton: {
800                toggleImageStatePanel();
801                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
802                        UsageStatistics.CATEGORY_BUTTON_PRESS,
803                        mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
804                return true;
805            }
806            case android.R.id.home: {
807                saveImage();
808                return true;
809            }
810        }
811        return false;
812    }
813
814    public void enableSave(boolean enable) {
815        if (mSaveButton != null)
816            mSaveButton.setEnabled(enable);
817    }
818
819    private void fillFx() {
820        FilterFxRepresentation nullFx =
821                new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
822        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
823        FiltersManager.getManager().addLooks(this, filtersRepresentations);
824
825        mCategoryLooksAdapter = new CategoryAdapter(this);
826        int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
827        mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
828        mCategoryLooksAdapter.add(new Action(this, nullFx, Action.FULL_VIEW));
829        for (FilterRepresentation representation : filtersRepresentations) {
830            mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
831        }
832    }
833
834    public void setDefaultPreset() {
835        // Default preset (original)
836        ImagePreset preset = new ImagePreset(); // empty
837        preset.setImageLoader(mImageLoader);
838
839        mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
840    }
841
842    // //////////////////////////////////////////////////////////////////////////////
843    // Some utility functions
844    // TODO: finish the cleanup.
845
846    public void invalidateViews() {
847        for (ImageShow views : mImageViews) {
848            views.invalidate();
849            views.updateImage();
850        }
851    }
852
853    public void hideImageViews() {
854        for (View view : mImageViews) {
855            view.setVisibility(View.GONE);
856        }
857        mEditorPlaceHolder.hide();
858    }
859
860    // //////////////////////////////////////////////////////////////////////////////
861    // imageState panel...
862
863    public void toggleImageStatePanel() {
864        invalidateOptionsMenu();
865        mShowingImageStatePanel = !mShowingImageStatePanel;
866        Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
867        if (panel != null) {
868            if (panel instanceof EditorPanel) {
869                EditorPanel editorPanel = (EditorPanel) panel;
870                editorPanel.showImageStatePanel(mShowingImageStatePanel);
871            } else if (panel instanceof MainPanel) {
872                MainPanel mainPanel = (MainPanel) panel;
873                mainPanel.showImageStatePanel(mShowingImageStatePanel);
874            }
875        }
876    }
877
878    @Override
879    public void onConfigurationChanged(Configuration newConfig)
880    {
881        super.onConfigurationChanged(newConfig);
882        setDefaultValues();
883        loadXML();
884        loadMainPanel();
885
886        // mLoadBitmapTask==null implies you have looked at the intent
887        if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
888            mCategoryFiltersAdapter.removeTinyPlanet();
889        }
890        final View loading = findViewById(R.id.loading);
891        loading.setVisibility(View.GONE);
892    }
893
894    public void setupMasterImage() {
895        mImageLoader = new ImageLoader(this, getApplicationContext());
896
897        HistoryManager mHistoryManager = new HistoryManager();
898        StateAdapter mImageStateAdapter = new StateAdapter(this, 0);
899        MasterImage.reset();
900        mMasterImage = MasterImage.getImage();
901        mMasterImage.setHistoryManager(mHistoryManager);
902        mMasterImage.setStateAdapter(mImageStateAdapter);
903        mMasterImage.setActivity(this);
904        mMasterImage.setImageLoader(mImageLoader);
905
906        if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
907            mMasterImage.setSupportsHighRes(true);
908        } else {
909            mMasterImage.setSupportsHighRes(false);
910        }
911    }
912
913    void resetHistory() {
914        HistoryManager adapter = mMasterImage.getHistory();
915        adapter.reset();
916        HistoryItem historyItem = adapter.getItem(0);
917        ImagePreset original = new ImagePreset(historyItem.getImagePreset());
918        mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
919        invalidateViews();
920        backToMain();
921    }
922
923    public void showDefaultImageView() {
924        mEditorPlaceHolder.hide();
925        mImageShow.setVisibility(View.VISIBLE);
926        MasterImage.getImage().setCurrentFilter(null);
927        MasterImage.getImage().setCurrentFilterRepresentation(null);
928    }
929
930    public void backToMain() {
931        Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
932        if (currentPanel instanceof MainPanel) {
933            return;
934        }
935        loadMainPanel();
936        showDefaultImageView();
937    }
938
939    @Override
940    public void onBackPressed() {
941        Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
942        if (currentPanel instanceof MainPanel) {
943            if (!mImageShow.hasModifications()) {
944                done();
945            } else {
946                AlertDialog.Builder builder = new AlertDialog.Builder(this);
947                builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
948                builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
949                    @Override
950                    public void onClick(DialogInterface dialog, int id) {
951                        saveImage();
952                    }
953                });
954                builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
955                    @Override
956                    public void onClick(DialogInterface dialog, int id) {
957                        done();
958                    }
959                });
960                builder.show();
961            }
962        } else {
963            backToMain();
964        }
965    }
966
967    public void cannotLoadImage() {
968        Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
969        finish();
970    }
971
972    // //////////////////////////////////////////////////////////////////////////////
973
974    public float getPixelsFromDip(float value) {
975        Resources r = getResources();
976        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
977                r.getDisplayMetrics());
978    }
979
980    @Override
981    public void onItemClick(AdapterView<?> parent, View view, int position,
982            long id) {
983        mMasterImage.onHistoryItemClick(position);
984        invalidateViews();
985    }
986
987    public void pickImage() {
988        Intent intent = new Intent();
989        intent.setType("image/*");
990        intent.setAction(Intent.ACTION_GET_CONTENT);
991        startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
992                SELECT_PICTURE);
993    }
994
995    @Override
996    public void onActivityResult(int requestCode, int resultCode, Intent data) {
997        if (resultCode == RESULT_OK) {
998            if (requestCode == SELECT_PICTURE) {
999                Uri selectedImageUri = data.getData();
1000                startLoadBitmap(selectedImageUri);
1001            }
1002        }
1003    }
1004
1005
1006    public void saveImage() {
1007        if (mImageShow.hasModifications()) {
1008            // Get the name of the album, to which the image will be saved
1009            File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mSelectedImageUri);
1010            int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1011            String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1012            showSavingProgress(albumName);
1013            mImageShow.saveImage(this, null);
1014        } else {
1015            done();
1016        }
1017    }
1018
1019
1020    public void done() {
1021        hideSavingProgress();
1022        if (mLoadBitmapTask != null) {
1023            mLoadBitmapTask.cancel(false);
1024        }
1025        finish();
1026    }
1027
1028    private void extractXMPData() {
1029        XMresults res = XmpPresets.extractXMPData(
1030                getBaseContext(), mMasterImage, getIntent().getData());
1031        if (res == null)
1032            return;
1033
1034        mOriginalImageUri = res.originalimage;
1035        mOriginalPreset = res.preset;
1036    }
1037
1038    public Uri getSelectedImageUri() {
1039        return mSelectedImageUri;
1040    }
1041
1042    static {
1043        System.loadLibrary("jni_filtershow_filters");
1044    }
1045
1046
1047}
1048