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