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