OSDN Git Service

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