OSDN Git Service

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