OSDN Git Service

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