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