FilterShowActivity.java revision 6019ad95f1c85786488d2d4cae1cbb665d33bd30
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.Activity;
21import android.app.ProgressDialog;
22import android.app.WallpaperManager;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.graphics.Color;
31import android.graphics.Point;
32import android.graphics.drawable.Drawable;
33import android.net.Uri;
34import android.os.AsyncTask;
35import android.os.Bundle;
36import android.provider.MediaStore;
37import android.util.DisplayMetrics;
38import android.util.Log;
39import android.util.TypedValue;
40import android.view.*;
41import android.view.View.OnClickListener;
42import android.widget.AdapterView;
43import android.widget.AdapterView.OnItemClickListener;
44import android.widget.FrameLayout;
45import android.widget.ImageButton;
46import android.widget.LinearLayout;
47import android.widget.ListView;
48import android.widget.ShareActionProvider;
49import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
50import android.widget.Toast;
51
52import com.android.gallery3d.R;
53import com.android.gallery3d.data.LocalAlbum;
54import com.android.gallery3d.filtershow.cache.FilteringPipeline;
55import com.android.gallery3d.filtershow.cache.ImageLoader;
56import com.android.gallery3d.filtershow.editors.BasicEditor;
57import com.android.gallery3d.filtershow.editors.EditorCrop;
58import com.android.gallery3d.filtershow.editors.EditorDraw;
59import com.android.gallery3d.filtershow.editors.EditorFlip;
60import com.android.gallery3d.filtershow.editors.EditorInfo;
61import com.android.gallery3d.filtershow.editors.EditorManager;
62import com.android.gallery3d.filtershow.editors.EditorRedEye;
63import com.android.gallery3d.filtershow.editors.EditorRotate;
64import com.android.gallery3d.filtershow.editors.EditorStraighten;
65import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
66import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
67import com.android.gallery3d.filtershow.filters.FilterColorBorderRepresentation;
68import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
69import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
70import com.android.gallery3d.filtershow.filters.FilterRepresentation;
71import com.android.gallery3d.filtershow.filters.FiltersManager;
72import com.android.gallery3d.filtershow.filters.ImageFilter;
73import com.android.gallery3d.filtershow.filters.ImageFilterBorder;
74import com.android.gallery3d.filtershow.filters.ImageFilterRS;
75import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
76import com.android.gallery3d.filtershow.imageshow.ImageCrop;
77import com.android.gallery3d.filtershow.imageshow.ImageShow;
78import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet;
79import com.android.gallery3d.filtershow.imageshow.ImageZoom;
80import com.android.gallery3d.filtershow.imageshow.MasterImage;
81import com.android.gallery3d.filtershow.presets.ImagePreset;
82import com.android.gallery3d.filtershow.provider.SharedImageProvider;
83import com.android.gallery3d.filtershow.tools.BitmapTask;
84import com.android.gallery3d.filtershow.tools.SaveCopyTask;
85import com.android.gallery3d.filtershow.ui.FilterIconButton;
86import com.android.gallery3d.filtershow.ui.FramedTextButton;
87import com.android.gallery3d.filtershow.ui.Spline;
88import com.android.gallery3d.util.GalleryUtils;
89
90import java.io.File;
91import java.io.IOException;
92import java.lang.ref.WeakReference;
93import java.util.Vector;
94
95public class FilterShowActivity extends Activity implements OnItemClickListener,
96        OnShareTargetSelectedListener {
97
98    // fields for supporting crop action
99    public static final String CROP_ACTION = "com.android.camera.action.CROP";
100    private CropExtras mCropExtras = null;
101    private String mAction = "";
102    MasterImage mMasterImage = null;
103
104    public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
105    public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
106    public static final int MAX_BMAP_IN_INTENT = 990000;
107    private final PanelController mPanelController = new PanelController();
108    private ImageLoader mImageLoader = null;
109    private ImageShow mImageShow = null;
110    private ImageTinyPlanet mImageTinyPlanet = null;
111
112    private View mSaveButton = null;
113
114    private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
115
116    private static final int SELECT_PICTURE = 1;
117    private static final String LOGTAG = "FilterShowActivity";
118    protected static final boolean ANIMATE_PANELS = true;
119    private static int mImageBorderSize = 4; // in percent
120
121    private boolean mShowingTinyPlanet = false;
122    private boolean mShowingHistoryPanel = 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 FilterIconButton mNullFxFilter;
136    private FilterIconButton mNullBorderFilter;
137    private int mIconSeedSize = 140;
138
139
140    @Override
141    public void onCreate(Bundle savedInstanceState) {
142        super.onCreate(savedInstanceState);
143
144        setupMasterImage();
145        setDefaultValues();
146        fillEditors();
147
148        loadXML();
149        if (getResources().getConfiguration().orientation
150                == Configuration.ORIENTATION_LANDSCAPE) {
151            mShowingImageStatePanel = true;
152        }
153
154        setDefaultPreset();
155
156        processIntent();
157    }
158
159    private void loadXML() {
160        setContentView(R.layout.filtershow_activity);
161
162        ActionBar actionBar = getActionBar();
163        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
164        actionBar.setCustomView(R.layout.filtershow_actionbar);
165
166        mSaveButton = actionBar.getCustomView();
167        mSaveButton.setOnClickListener(new OnClickListener() {
168            @Override
169            public void onClick(View view) {
170                saveImage();
171            }
172        });
173
174        mImageShow = (ImageShow) findViewById(R.id.imageShow);
175        mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet);
176        mImageViews.add(mImageShow);
177        mImageViews.add(mImageTinyPlanet);
178
179        setupEditors();
180
181        mEditorPlaceHolder.hide();
182
183        mImageShow.setImageLoader(mImageLoader);
184        mImageTinyPlanet.setImageLoader(mImageLoader);
185
186        mPanelController.clear();
187        mPanelController.setActivity(this);
188        mPanelController.setEditorPlaceHolder(mEditorPlaceHolder);
189
190        mPanelController.addImageView(findViewById(R.id.imageShow));
191        mPanelController.addImageView(findViewById(R.id.imageTinyPlanet));
192
193        mPanelController.addPanel(R.id.fxButton, R.id.fxList, 0);
194        mPanelController.addPanel(R.id.borderButton, R.id.bordersList, 1);
195        mPanelController.addPanel(R.id.geometryButton, R.id.geometryList, 2);
196        mPanelController.addPanel(R.id.colorsButton, R.id.colorsFxList, 3);
197
198        fillFx((LinearLayout) findViewById(R.id.listFilters), R.id.fxButton);
199        LoadBordersTask loadBorders = new LoadBordersTask((LinearLayout) findViewById(R.id.listBorders));
200        loadBorders.execute();
201        fillGeometry();
202        fillFilters();
203
204        mPanelController.addView(findViewById(R.id.applyEffect));
205
206        ((ViewStub) findViewById(R.id.historyPanelStub)).inflate();
207        ((ViewStub) findViewById(R.id.statePanelStub)).inflate();
208        setupHistoryPanel();
209        setupStatePanel();
210
211        mPanelController.setRowPanel(findViewById(R.id.secondRowPanel));
212        mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList),
213                findViewById(R.id.panelAccessoryViewList),
214                findViewById(R.id.applyEffect));
215
216        mPanelController.setCurrentPanel(R.id.fxButton);
217    }
218
219    public void setupHistoryPanel() {
220        findViewById(R.id.resetOperationsButton).setOnClickListener(
221                createOnClickResetOperationsButton());
222
223        ListView operationsList = (ListView) findViewById(R.id.operationsList);
224        operationsList.setAdapter(mMasterImage.getHistory());
225        operationsList.setOnItemClickListener(this);
226    }
227
228    public void setupStatePanel() {
229        ListView imageStateList = (ListView) findViewById(R.id.imageStateList);
230        imageStateList.setAdapter(mMasterImage.getState());
231        mImageLoader.setAdapter(mMasterImage.getHistory());
232    }
233
234    private void fillPanel(Vector<FilterRepresentation> representations, int layoutId, int buttonId) {
235        ImageButton button = (ImageButton) findViewById(buttonId);
236        LinearLayout layout = (LinearLayout) findViewById(layoutId);
237
238        for (FilterRepresentation representation : representations) {
239            setupFilterRepresentationButton(representation, layout, button);
240        }
241    }
242
243    private void fillFilters() {
244        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
245        FiltersManager filtersManager = FiltersManager.getManager();
246        filtersManager.addEffects(filtersRepresentations);
247        fillPanel(filtersRepresentations, R.id.listColorsFx, R.id.colorsButton);
248    }
249
250    private void fillGeometry() {
251        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
252        FiltersManager filtersManager = FiltersManager.getManager();
253
254        GeometryMetadata geo = new GeometryMetadata();
255        int[] editorsId = geo.getEditorIds();
256        for (int i = 0; i < editorsId.length; i++) {
257            int editorId = editorsId[i];
258            GeometryMetadata geometry = new GeometryMetadata(geo);
259            geometry.setEditorId(editorId);
260            EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId);
261            geometry.setTextId(editorInfo.getTextId());
262            geometry.setOverlayId(editorInfo.getOverlayId());
263            geometry.setOverlayOnly(editorInfo.getOverlayOnly());
264            filtersRepresentations.add(geometry);
265        }
266
267        filtersManager.addTools(filtersRepresentations);
268        fillPanel(filtersRepresentations, R.id.listGeometry, R.id.geometryButton);
269    }
270
271    private void processIntent() {
272        Intent intent = getIntent();
273        if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
274            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
275        }
276
277        mAction = intent.getAction();
278
279        if (intent.getData() != null) {
280            startLoadBitmap(intent.getData());
281        } else {
282            pickImage();
283        }
284
285        // Handle behavior for various actions
286        if (mAction.equalsIgnoreCase(CROP_ACTION)) {
287            Bundle extras = intent.getExtras();
288            if (extras != null) {
289                mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
290                        extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
291                        extras.getBoolean(CropExtras.KEY_SCALE, true) &&
292                                extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
293                        extras.getInt(CropExtras.KEY_ASPECT_X, 0),
294                        extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
295                        extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
296                        extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
297                        (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
298                        extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
299                        extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
300                        extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
301                        extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
302
303                if (mCropExtras.getShowWhenLocked()) {
304                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
305                }
306                mImageShow.getImagePreset().mGeoData.setCropExtras(mCropExtras);
307
308                // FIXME: moving to editors breaks the crop action
309                EditorCrop crop = (EditorCrop) mEditorPlaceHolder.getEditor(EditorCrop.ID);
310
311                crop.setExtras(mCropExtras);
312                String s = getString(R.string.Fixed);
313                crop.setAspectString(s);
314                crop.setCropActionFlag(true);
315                mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0
316                        && mCropExtras.getAspectY() > 0);
317            }
318        }
319    }
320
321    private void setupEditors() {
322        mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
323        EditorManager.addEditors(mEditorPlaceHolder);
324        mEditorPlaceHolder.setOldViews(mImageViews);
325        mEditorPlaceHolder.setImageLoader(mImageLoader);
326    }
327
328    private void fillEditors() {
329        mEditorPlaceHolder.addEditor(new EditorDraw());
330        mEditorPlaceHolder.addEditor(new BasicEditor());
331        mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
332        mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
333        mEditorPlaceHolder.addEditor(new EditorRedEye());
334        mEditorPlaceHolder.addEditor(new EditorCrop());
335        mEditorPlaceHolder.addEditor(new EditorFlip());
336        mEditorPlaceHolder.addEditor(new EditorRotate());
337        mEditorPlaceHolder.addEditor(new EditorStraighten());
338    }
339
340    private void setDefaultValues() {
341        ImageFilter.setActivityForMemoryToasts(this);
342        ImageFilterRS.setRenderScriptContext(this);
343
344        Resources res = getResources();
345        ImageFilterBorder filterBorder = (ImageFilterBorder) FiltersManager.getManager().getFilter(ImageFilterBorder.class);
346        filterBorder.setResources(res);
347        filterBorder = (ImageFilterBorder) FiltersManager.getPreviewManager().getFilter(ImageFilterBorder.class);
348        filterBorder.setResources(res);
349
350        ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen));
351        // TODO: get those values from XML.
352        ImageZoom.setZoomedSize(getPixelsFromDip(256));
353        FramedTextButton.setTextSize((int) getPixelsFromDip(14));
354        FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
355        FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
356        ImageShow.setTextSize((int) getPixelsFromDip(12));
357        ImageShow.setTextPadding((int) getPixelsFromDip(10));
358        ImageShow.setOriginalTextMargin((int) getPixelsFromDip(4));
359        ImageShow.setOriginalTextSize((int) getPixelsFromDip(18));
360        ImageShow.setOriginalText(res.getString(R.string.original_picture_text));
361        mIconSeedSize = res.getDimensionPixelSize(R.dimen.thumbnail_size);
362        // TODO: pick correct value
363        // MasterImage.setIconSeedSize(mIconSeedSize);
364
365        Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
366        int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
367        Spline.setCurveHandle(curveHandle, curveHandleSize);
368        Spline.setCurveWidth((int) getPixelsFromDip(3));
369
370        ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
371        ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
372        ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
373    }
374
375    private void startLoadBitmap(Uri uri) {
376        final View filters = findViewById(R.id.filtersPanel);
377        final View loading = findViewById(R.id.loading);
378        final View imageShow = findViewById(R.id.imageShow);
379        imageShow.setVisibility(View.INVISIBLE);
380        filters.setVisibility(View.INVISIBLE);
381        loading.setVisibility(View.VISIBLE);
382
383        View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
384        if (tinyPlanetView != null) {
385            mShowingTinyPlanet = false;
386            tinyPlanetView.setVisibility(View.GONE);
387        }
388        mLoadBitmapTask = new LoadBitmapTask(tinyPlanetView);
389        mLoadBitmapTask.execute(uri);
390    }
391
392    private class LoadBordersTask extends AsyncTask<Void, Boolean, Boolean> {
393        Vector<FilterRepresentation> mBorders;
394        LinearLayout mList;
395
396        public LoadBordersTask(LinearLayout list) {
397            mList = list;
398            mBorders = new Vector<FilterRepresentation>();
399        }
400
401        @Override
402        protected Boolean doInBackground(Void... params) {
403            mBorders.add(new FilterImageBorderRepresentation(0));
404            mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5));
405            mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush));
406            mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge));
407            mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e));
408            mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape));
409            mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0));
410            mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, mImageBorderSize));
411            mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0));
412            mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, mImageBorderSize));
413            int creamColor = Color.argb(255, 237, 237, 227);
414            mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0));
415            mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, mImageBorderSize));
416            return true;
417        }
418
419        @Override
420        protected void onPostExecute(Boolean result) {
421            if (!result) {
422                return;
423            }
424            for (int i = 0; i < mBorders.size(); i++) {
425                FilterRepresentation filter = mBorders.elementAt(i);
426                filter.setName(getString(R.string.borders));
427                if (i == 0) {
428                    filter.setName(getString(R.string.none));
429                }
430                ImageButton borderButton = (ImageButton) findViewById(R.id.borderButton);
431                FilterIconButton b = setupFilterRepresentationButton(filter, mList, borderButton);
432                if (i == 0) {
433                    mNullBorderFilter = b;
434                    mNullBorderFilter.setSelected(true);
435                }
436            }
437            fillButtonIcons();
438        }
439    }
440
441    private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
442        View mTinyPlanetButton;
443        int mBitmapSize;
444
445        public LoadBitmapTask(View button) {
446            mTinyPlanetButton = button;
447            mBitmapSize = getScreenImageSize();
448        }
449
450        @Override
451        protected Boolean doInBackground(Uri... params) {
452            if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
453                return false;
454            }
455            publishProgress(mImageLoader.queryLightCycle360());
456            return true;
457        }
458
459        @Override
460        protected void onProgressUpdate(Boolean... values) {
461            super.onProgressUpdate(values);
462            if (isCancelled()) {
463                return;
464            }
465            if (values[0]) {
466                mShowingTinyPlanet = true;
467                mTinyPlanetButton.setVisibility(View.VISIBLE);
468            }
469        }
470
471        @Override
472        protected void onPostExecute(Boolean result) {
473
474            if (isCancelled()) {
475                return;
476            }
477
478            if (!result) {
479                cannotLoadImage();
480            }
481
482            final View loading = findViewById(R.id.loading);
483            loading.setVisibility(View.GONE);
484            final View filters = findViewById(R.id.filtersPanel);
485            filters.setVisibility(View.VISIBLE);
486            if (PanelController.useAnimations()) {
487                float y = filters.getY();
488                filters.setY(y + filters.getHeight());
489                filters.animate().setDuration(600).y(y).withLayer().start();
490            }
491            final View imageShow = findViewById(R.id.imageShow);
492            imageShow.setVisibility(View.VISIBLE);
493
494            Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
495            FilteringPipeline pipeline = FilteringPipeline.getPipeline();
496            pipeline.setOriginal(largeBitmap);
497            float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
498            pipeline.setPreviewScaleFactor(previewScale);
499
500            fillButtonIcons();
501            MasterImage.getImage().setOriginalGeometry(largeBitmap);
502            mLoadBitmapTask = null;
503
504            if (mAction == CROP_ACTION) {
505                mPanelController.showComponent(findViewById(EditorCrop.ID));
506            } else if (mAction == TINY_PLANET_ACTION) {
507                mPanelController.showComponent(findViewById(EditorTinyPlanet.ID));
508            }
509
510            super.onPostExecute(result);
511        }
512
513    }
514
515    private void fillButtonIcons() {
516        Bitmap bmap = mImageLoader.getOriginalBitmapSmall();
517        if (bmap != null && bmap.getWidth() > 0 && bmap.getHeight() > 0) {
518            float w = bmap.getWidth();
519            float h = bmap.getHeight();
520            float f = mIconSeedSize / Math.min(w, h);
521            w = w * f;
522            h = h * f;
523            bmap = Bitmap.createScaledBitmap(bmap, (int) w, (int) h, true);
524
525            LinearLayout listColors = (LinearLayout) findViewById(R.id.listColorsFx);
526            int num_colors_buttons = listColors.getChildCount();
527            for (int i = 0; i < num_colors_buttons; i++) {
528                FilterIconButton b = (FilterIconButton) listColors.getChildAt(i);
529                b.setIcon(bmap);
530            }
531
532            LinearLayout listFilters = (LinearLayout) findViewById(R.id.listFilters);
533            int num_filters_buttons = listFilters.getChildCount();
534            for (int i = 0; i < num_filters_buttons; i++) {
535                FilterIconButton b = (FilterIconButton) listFilters.getChildAt(i);
536                b.setIcon(bmap);
537            }
538
539            LinearLayout listBorders = (LinearLayout) findViewById(R.id.listBorders);
540            int num_borders_buttons = listBorders.getChildCount();
541            for (int i = 0; i < num_borders_buttons; i++) {
542                FilterIconButton b = (FilterIconButton) listBorders.getChildAt(i);
543                b.setIcon(bmap);
544            }
545
546        }
547    }
548
549    @Override
550    protected void onDestroy() {
551        if (mLoadBitmapTask != null) {
552            mLoadBitmapTask.cancel(false);
553        }
554        // TODO:  Using singletons is a bad design choice for many of these
555        // due static reference leaks and in general.  Please refactor.
556        MasterImage.reset();
557        FilteringPipeline.reset();
558        ImageFilter.resetStatics();
559        FiltersManager.reset();
560        super.onDestroy();
561    }
562
563    private int translateMainPanel(View viewPanel) {
564        int accessoryPanelWidth = viewPanel.getWidth();
565        int mainViewWidth = findViewById(R.id.mainView).getWidth();
566        int mainPanelWidth = mImageShow.getDisplayedImageBounds().width();
567        if (mainPanelWidth == 0) {
568            mainPanelWidth = mainViewWidth;
569        }
570        int filtersPanelWidth = findViewById(R.id.filtersPanel).getWidth();
571        if (mainPanelWidth < filtersPanelWidth) {
572            mainPanelWidth = filtersPanelWidth;
573        }
574        int leftOver = mainViewWidth - mainPanelWidth - accessoryPanelWidth;
575        if (leftOver < 0) {
576            return -accessoryPanelWidth;
577        }
578        return 0;
579    }
580
581    private int getScreenImageSize() {
582        DisplayMetrics metrics = new DisplayMetrics();
583        Display display = getWindowManager().getDefaultDisplay();
584        Point size = new Point();
585        display.getSize(size);
586        display.getMetrics(metrics);
587        int msize = Math.min(size.x, size.y);
588        return (133 * msize) / metrics.densityDpi;
589    }
590
591    private void showSavingProgress(String albumName) {
592        ProgressDialog progress;
593        if (mSavingProgressDialog != null) {
594            progress = mSavingProgressDialog.get();
595            if (progress != null) {
596                progress.show();
597                return;
598            }
599        }
600        // TODO: Allow cancellation of the saving process
601        String progressText;
602        if (albumName == null) {
603            progressText = getString(R.string.saving_image);
604        } else {
605            progressText = getString(R.string.filtershow_saving_image, albumName);
606        }
607        progress = ProgressDialog.show(this, "", progressText, true, false);
608        mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
609    }
610
611    private void hideSavingProgress() {
612        if (mSavingProgressDialog != null) {
613            ProgressDialog progress = mSavingProgressDialog.get();
614            if (progress != null)
615                progress.dismiss();
616        }
617    }
618
619    public void completeSaveImage(Uri saveUri) {
620        if (mSharingImage && mSharedOutputFile != null) {
621            // Image saved, we unblock the content provider
622            Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
623                    Uri.encode(mSharedOutputFile.getAbsolutePath()));
624            ContentValues values = new ContentValues();
625            values.put(SharedImageProvider.PREPARE, false);
626            getContentResolver().insert(uri, values);
627        }
628        setResult(RESULT_OK, new Intent().setData(saveUri));
629        hideSavingProgress();
630        finish();
631    }
632
633    @Override
634    public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
635        // First, let's tell the SharedImageProvider that it will need to wait
636        // for the image
637        Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
638                Uri.encode(mSharedOutputFile.getAbsolutePath()));
639        ContentValues values = new ContentValues();
640        values.put(SharedImageProvider.PREPARE, true);
641        getContentResolver().insert(uri, values);
642        mSharingImage = true;
643
644        // Process and save the image in the background.
645        showSavingProgress(null);
646        mImageShow.saveImage(this, mSharedOutputFile);
647        return true;
648    }
649
650    private Intent getDefaultShareIntent() {
651        Intent intent = new Intent(Intent.ACTION_SEND);
652        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
653        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
654        intent.setType(SharedImageProvider.MIME_TYPE);
655        mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri());
656        Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
657                Uri.encode(mSharedOutputFile.getAbsolutePath()));
658        intent.putExtra(Intent.EXTRA_STREAM, uri);
659        return intent;
660    }
661
662    @Override
663    public boolean onCreateOptionsMenu(Menu menu) {
664        getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
665        MenuItem showHistory = menu.findItem(R.id.operationsButton);
666        if (mShowingHistoryPanel) {
667            showHistory.setTitle(R.string.hide_history_panel);
668        } else {
669            showHistory.setTitle(R.string.show_history_panel);
670        }
671        MenuItem showState = menu.findItem(R.id.showImageStateButton);
672        if (mShowingImageStatePanel) {
673            showState.setTitle(R.string.hide_imagestate_panel);
674        } else {
675            showState.setTitle(R.string.show_imagestate_panel);
676        }
677        mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
678                .getActionProvider();
679        mShareActionProvider.setShareIntent(getDefaultShareIntent());
680        mShareActionProvider.setOnShareTargetSelectedListener(this);
681
682        MenuItem undoItem = menu.findItem(R.id.undoButton);
683        MenuItem redoItem = menu.findItem(R.id.redoButton);
684        MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
685        mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
686        return true;
687    }
688
689    @Override
690    public void onPause() {
691        super.onPause();
692        if (mShareActionProvider != null) {
693            mShareActionProvider.setOnShareTargetSelectedListener(null);
694        }
695    }
696
697    @Override
698    public void onResume() {
699        super.onResume();
700        if (mShareActionProvider != null) {
701            mShareActionProvider.setOnShareTargetSelectedListener(this);
702        }
703    }
704
705    @Override
706    public boolean onOptionsItemSelected(MenuItem item) {
707        switch (item.getItemId()) {
708            case R.id.undoButton: {
709                mPanelController.resetParameters();
710                HistoryAdapter adapter = mMasterImage.getHistory();
711                int position = adapter.undo();
712                mMasterImage.onHistoryItemClick(position);
713                mImageShow.showToast("Undo");
714                invalidateViews();
715                return true;
716            }
717            case R.id.redoButton: {
718                HistoryAdapter adapter = mMasterImage.getHistory();
719                int position = adapter.redo();
720                mMasterImage.onHistoryItemClick(position);
721                mImageShow.showToast("Redo");
722                invalidateViews();
723                return true;
724            }
725            case R.id.resetHistoryButton: {
726                resetHistory();
727                return true;
728            }
729            case R.id.showImageStateButton: {
730                toggleImageStatePanel();
731                return true;
732            }
733            case R.id.operationsButton: {
734                toggleHistoryPanel();
735                return true;
736            }
737            case android.R.id.home: {
738                saveImage();
739                return true;
740            }
741        }
742        return false;
743    }
744
745    public void enableSave(boolean enable) {
746        if (mSaveButton != null)
747            mSaveButton.setEnabled(enable);
748    }
749
750    public FilterIconButton setupFilterRepresentationButton(FilterRepresentation representation, LinearLayout panel, View button) {
751        LayoutInflater inflater =
752                (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
753        FilterIconButton icon = (FilterIconButton) inflater.inflate(R.layout.filtericonbutton,
754                panel, false);
755        if (representation.getTextId() != 0) {
756            representation.setName(getString(representation.getTextId()));
757        }
758        String text = representation.getName();
759        icon.setup(text, mPanelController, panel);
760        icon.setFilterRepresentation(representation);
761        icon.setId(representation.getEditorId());
762        mPanelController.addComponent(button, icon);
763        panel.addView(icon);
764        return icon;
765    }
766
767    private void fillFx(LinearLayout listFilters, int buttonId) {
768        // TODO: use listview
769        // TODO: load the filters straight from the filesystem
770
771        FilterFxRepresentation[] fxArray = new FilterFxRepresentation[18];
772        int p = 0;
773
774        int[] drawid = {
775                R.drawable.filtershow_fx_0005_punch,
776                R.drawable.filtershow_fx_0000_vintage,
777                R.drawable.filtershow_fx_0004_bw_contrast,
778                R.drawable.filtershow_fx_0002_bleach,
779                R.drawable.filtershow_fx_0001_instant,
780                R.drawable.filtershow_fx_0007_washout,
781                R.drawable.filtershow_fx_0003_blue_crush,
782                R.drawable.filtershow_fx_0008_washout_color,
783                R.drawable.filtershow_fx_0006_x_process
784        };
785
786        int[] fxNameid = {
787                R.string.ffx_punch,
788                R.string.ffx_vintage,
789                R.string.ffx_bw_contrast,
790                R.string.ffx_bleach,
791                R.string.ffx_instant,
792                R.string.ffx_washout,
793                R.string.ffx_blue_crush,
794                R.string.ffx_washout_color,
795                R.string.ffx_x_process
796        };
797
798        BitmapFactory.Options o = new BitmapFactory.Options();
799        o.inScaled = false;
800
801        for (int i = 0; i < drawid.length; i++) {
802            Bitmap b = BitmapFactory.decodeResource(getResources(), drawid[i], o);
803            FilterFxRepresentation fx = new FilterFxRepresentation(getString(fxNameid[i]), drawid[i], fxNameid[i]);
804            fx.setFxBitmap(b);
805            fxArray[p++] = fx;
806        }
807
808        ImageButton button = (ImageButton) findViewById(buttonId);
809
810        FilterFxRepresentation nullFx = new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
811        mNullFxFilter = setupFilterRepresentationButton(nullFx, listFilters, button);
812        mNullFxFilter.setSelected(true);
813
814        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
815        FiltersManager.getManager().addLooks(filtersRepresentations);
816        for (FilterRepresentation representation : filtersRepresentations) {
817            setupFilterRepresentationButton(representation, listFilters, button);
818        }
819
820        for (int i = 0; i < p; i++) {
821            setupFilterRepresentationButton(fxArray[i], listFilters, button);
822        }
823    }
824
825    public void setDefaultPreset() {
826        // Default preset (original)
827        ImagePreset preset = new ImagePreset(getString(R.string.history_original)); // empty
828        preset.setImageLoader(mImageLoader);
829
830        mMasterImage.setPreset(preset, true);
831    }
832
833    // //////////////////////////////////////////////////////////////////////////////
834    // Some utility functions
835    // TODO: finish the cleanup.
836
837    public void invalidateViews() {
838        for (ImageShow views : mImageViews) {
839            views.invalidate();
840            views.updateImage();
841        }
842    }
843
844    public void hideImageViews() {
845        for (View view : mImageViews) {
846            view.setVisibility(View.GONE);
847        }
848        mEditorPlaceHolder.hide();
849    }
850
851    // //////////////////////////////////////////////////////////////////////////////
852    // imageState panel...
853
854    public boolean isShowingHistoryPanel() {
855        return mShowingHistoryPanel;
856    }
857
858    private void toggleImageStatePanel() {
859        final View viewList = findViewById(R.id.imageStatePanel);
860
861        if (mShowingHistoryPanel) {
862            findViewById(R.id.historyPanel).setVisibility(View.GONE);
863            mShowingHistoryPanel = false;
864        }
865
866        if (!mShowingImageStatePanel) {
867            mShowingImageStatePanel = true;
868            viewList.setVisibility(View.VISIBLE);
869        } else {
870            mShowingImageStatePanel = false;
871            viewList.setVisibility(View.GONE);
872        }
873        invalidateOptionsMenu();
874    }
875
876    @Override
877    public void onConfigurationChanged(Configuration newConfig)
878    {
879        super.onConfigurationChanged(newConfig);
880        setDefaultValues();
881        loadXML();
882        if (getResources().getConfiguration().orientation
883                == Configuration.ORIENTATION_LANDSCAPE) {
884            mShowingImageStatePanel = true;
885        }
886        if (mShowingHistoryPanel) {
887            toggleHistoryPanel();
888        }
889        if (mShowingTinyPlanet == false) {
890            View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
891            if (tinyPlanetView != null) {
892                tinyPlanetView.setVisibility(View.GONE);
893            }
894        }
895        final View loading = findViewById(R.id.loading);
896        loading.setVisibility(View.GONE);
897    }
898
899    public void setupMasterImage() {
900        mImageLoader = new ImageLoader(this, getApplicationContext());
901
902        HistoryAdapter mHistoryAdapter = new HistoryAdapter(
903                this, R.layout.filtershow_history_operation_row,
904                R.id.rowTextView);
905        ImageStateAdapter mImageStateAdapter = new ImageStateAdapter(this,
906                R.layout.filtershow_imagestate_row);
907
908        MasterImage.reset();
909        mMasterImage = MasterImage.getImage();
910        mMasterImage.setHistoryAdapter(mHistoryAdapter);
911        mMasterImage.setStateAdapter(mImageStateAdapter);
912        mMasterImage.setActivity(this);
913    }
914
915    // //////////////////////////////////////////////////////////////////////////////
916    // history panel...
917
918    public void toggleHistoryPanel() {
919        final View view = findViewById(R.id.mainPanel);
920        final View viewList = findViewById(R.id.historyPanel);
921
922        if (mShowingImageStatePanel) {
923            findViewById(R.id.imageStatePanel).setVisibility(View.GONE);
924        }
925
926        int translate = translateMainPanel(viewList);
927        if (!mShowingHistoryPanel) {
928            mShowingHistoryPanel = true;
929            if (getResources().getConfiguration().orientation
930                    == Configuration.ORIENTATION_PORTRAIT) {
931                // If portrait, always remove the state panel
932                mShowingImageStatePanel = false;
933                if (PanelController.useAnimations()) {
934                    view.animate().setDuration(200).x(translate)
935                            .withLayer().withEndAction(new Runnable() {
936                        @Override
937                        public void run() {
938                            viewList.setAlpha(0);
939                            viewList.setVisibility(View.VISIBLE);
940                            viewList.animate().setDuration(100)
941                                    .alpha(1.0f).start();
942                        }
943                    }).start();
944                } else {
945                    view.setX(translate);
946                    viewList.setAlpha(0);
947                    viewList.setVisibility(View.VISIBLE);
948                    viewList.animate().setDuration(100)
949                            .alpha(1.0f).start();
950                }
951            } else {
952                findViewById(R.id.filtersPanel).setVisibility(View.GONE);
953                viewList.setVisibility(View.VISIBLE);
954            }
955        } else {
956            mShowingHistoryPanel = false;
957            if (getResources().getConfiguration().orientation
958                    == Configuration.ORIENTATION_PORTRAIT) {
959                viewList.setVisibility(View.INVISIBLE);
960                if (PanelController.useAnimations()) {
961                    view.animate().setDuration(200).x(0).withLayer()
962                            .start();
963                } else {
964                    view.setX(0);
965                }
966            } else {
967                viewList.setVisibility(View.GONE);
968                findViewById(R.id.filtersPanel).setVisibility(View.VISIBLE);
969                // In landscape, bring back the state panel if it was there
970                if (mShowingImageStatePanel) {
971                    findViewById(R.id.imageStatePanel).setVisibility(View.VISIBLE);
972                }
973            }
974        }
975        invalidateOptionsMenu();
976    }
977
978    public void dispatchNullFilterClick() {
979        mNullFxFilter.onClick(mNullFxFilter);
980        mNullBorderFilter.onClick(mNullBorderFilter);
981    }
982
983    void resetHistory() {
984        dispatchNullFilterClick();
985        HistoryAdapter adapter = mMasterImage.getHistory();
986        adapter.reset();
987        ImagePreset original = new ImagePreset(adapter.getItem(0));
988        mMasterImage.setPreset(original, true);
989        mPanelController.resetParameters();
990        invalidateViews();
991    }
992
993    // reset button in the history panel.
994    private OnClickListener createOnClickResetOperationsButton() {
995        return new View.OnClickListener() {
996            @Override
997            public void onClick(View v) {
998                resetHistory();
999            }
1000        };
1001    }
1002
1003    @Override
1004    public void onBackPressed() {
1005        if (mPanelController.onBackPressed()) {
1006            saveImage();
1007        }
1008    }
1009
1010    public PanelController getPanelController() {
1011        return mPanelController;
1012    }
1013
1014    public void cannotLoadImage() {
1015        CharSequence text = getString(R.string.cannot_load_image);
1016        Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
1017        toast.show();
1018        finish();
1019    }
1020
1021    // //////////////////////////////////////////////////////////////////////////////
1022
1023    public float getPixelsFromDip(float value) {
1024        Resources r = getResources();
1025        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1026                r.getDisplayMetrics());
1027    }
1028
1029    @Override
1030    public void onItemClick(AdapterView<?> parent, View view, int position,
1031            long id) {
1032        mMasterImage.onHistoryItemClick(position);
1033        invalidateViews();
1034    }
1035
1036    public void pickImage() {
1037        Intent intent = new Intent();
1038        intent.setType("image/*");
1039        intent.setAction(Intent.ACTION_GET_CONTENT);
1040        startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1041                SELECT_PICTURE);
1042    }
1043
1044    @Override
1045    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1046        if (resultCode == RESULT_OK) {
1047            if (requestCode == SELECT_PICTURE) {
1048                Uri selectedImageUri = data.getData();
1049                startLoadBitmap(selectedImageUri);
1050            }
1051        }
1052    }
1053
1054    private boolean mSaveToExtraUri = false;
1055    private boolean mSaveAsWallpaper = false;
1056    private boolean mReturnAsExtra = false;
1057    private boolean mOutputted = false;
1058
1059    public void saveImage() {
1060        if (mCropExtras != null) {
1061            if (mCropExtras.getExtraOutput() != null) {
1062                mSaveToExtraUri = true;
1063                mOutputted = true;
1064            }
1065            if (mCropExtras.getSetAsWallpaper()) {
1066                mSaveAsWallpaper = true;
1067                mOutputted = true;
1068            }
1069            if (mCropExtras.getReturnData()) {
1070
1071                mReturnAsExtra = true;
1072                mOutputted = true;
1073            }
1074
1075            if (mOutputted) {
1076                mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true);
1077                showSavingProgress(null);
1078                mImageShow.returnFilteredResult(this);
1079            }
1080        }
1081        if (!mOutputted) {
1082            if (mImageShow.hasModifications()) {
1083                // Get the name of the album, to which the image will be saved
1084                File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
1085                int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1086                String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1087                showSavingProgress(albumName);
1088                mImageShow.saveImage(this, null);
1089            } else {
1090                done();
1091            }
1092        }
1093    }
1094
1095    public void onFilteredResult(Bitmap filtered) {
1096        Intent intent = new Intent();
1097        intent.putExtra(CropExtras.KEY_CROPPED_RECT, mImageShow.getImageCropBounds());
1098        if (mSaveToExtraUri) {
1099            mImageShow.saveToUri(filtered, mCropExtras.getExtraOutput(),
1100                    mCropExtras.getOutputFormat(), this);
1101        }
1102        if (mSaveAsWallpaper) {
1103            setWallpaperInBackground(filtered);
1104        }
1105        if (mReturnAsExtra) {
1106            if (filtered != null) {
1107                int bmapSize = filtered.getRowBytes() * filtered.getHeight();
1108                /*
1109                 * Max size of Binder transaction buffer is 1Mb, so constrain
1110                 * Bitmap to be somewhat less than this, otherwise we get
1111                 * TransactionTooLargeExceptions.
1112                 */
1113                if (bmapSize > MAX_BMAP_IN_INTENT) {
1114                    Log.w(LOGTAG, "Bitmap too large to be returned via intent");
1115                } else {
1116                    intent.putExtra(CropExtras.KEY_DATA, filtered);
1117                }
1118            }
1119        }
1120        setResult(RESULT_OK, intent);
1121        if (!mSaveToExtraUri) {
1122            done();
1123        }
1124    }
1125
1126    void setWallpaperInBackground(final Bitmap bmap) {
1127        Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
1128        BitmapTask.Callbacks<FilterShowActivity> cb = new BitmapTask.Callbacks<FilterShowActivity>() {
1129            @Override
1130            public void onComplete(Bitmap result) {}
1131
1132            @Override
1133            public void onCancel() {}
1134
1135            @Override
1136            public Bitmap onExecute(FilterShowActivity param) {
1137                try {
1138                    WallpaperManager.getInstance(param).setBitmap(bmap);
1139                } catch (IOException e) {
1140                    Log.w(LOGTAG, "fail to set wall paper", e);
1141                }
1142                return null;
1143            }
1144        };
1145        (new BitmapTask<FilterShowActivity>(cb)).execute(this);
1146    }
1147
1148    public void done() {
1149        if (mOutputted) {
1150            hideSavingProgress();
1151        }
1152        finish();
1153    }
1154
1155    static {
1156        System.loadLibrary("jni_filtershow_filters");
1157    }
1158
1159}
1160