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