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