OSDN Git Service

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