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