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