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