OSDN Git Service

Add more metrics to Editor
[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.AlertDialog;
21 import android.app.ProgressDialog;
22 import android.content.ContentValues;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.res.Configuration;
27 import android.content.res.Resources;
28 import android.graphics.Bitmap;
29 import android.graphics.Point;
30 import android.graphics.drawable.Drawable;
31 import android.net.Uri;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.support.v4.app.Fragment;
36 import android.support.v4.app.FragmentActivity;
37 import android.support.v4.app.FragmentTransaction;
38 import android.util.DisplayMetrics;
39 import android.util.TypedValue;
40 import android.util.Log;
41 import android.view.Display;
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.ViewPropertyAnimator;
47 import android.view.WindowManager;
48 import android.widget.AdapterView;
49 import android.widget.AdapterView.OnItemClickListener;
50 import android.widget.FrameLayout;
51 import android.widget.ShareActionProvider;
52 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
53 import android.widget.Toast;
54
55 import com.android.gallery3d.R;
56 import com.android.gallery3d.data.LocalAlbum;
57 import com.android.gallery3d.filtershow.cache.CachingPipeline;
58 import com.android.gallery3d.filtershow.cache.FilteringPipeline;
59 import com.android.gallery3d.filtershow.cache.ImageLoader;
60 import com.android.gallery3d.filtershow.category.Action;
61 import com.android.gallery3d.filtershow.category.CategoryAdapter;
62 import com.android.gallery3d.filtershow.category.CategoryView;
63 import com.android.gallery3d.filtershow.category.MainPanel;
64 import com.android.gallery3d.filtershow.editors.BasicEditor;
65 import com.android.gallery3d.filtershow.editors.Editor;
66 import com.android.gallery3d.filtershow.editors.EditorCrop;
67 import com.android.gallery3d.filtershow.editors.EditorDraw;
68 import com.android.gallery3d.filtershow.editors.EditorFlip;
69 import com.android.gallery3d.filtershow.editors.EditorInfo;
70 import com.android.gallery3d.filtershow.editors.EditorManager;
71 import com.android.gallery3d.filtershow.editors.EditorPanel;
72 import com.android.gallery3d.filtershow.editors.EditorRedEye;
73 import com.android.gallery3d.filtershow.editors.EditorRotate;
74 import com.android.gallery3d.filtershow.editors.EditorStraighten;
75 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
76 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
77 import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
78 import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
79 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
80 import com.android.gallery3d.filtershow.filters.FiltersManager;
81 import com.android.gallery3d.filtershow.filters.ImageFilter;
82 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
83 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
84 import com.android.gallery3d.filtershow.imageshow.ImageShow;
85 import com.android.gallery3d.filtershow.imageshow.MasterImage;
86 import com.android.gallery3d.filtershow.presets.ImagePreset;
87 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
88 import com.android.gallery3d.filtershow.state.StateAdapter;
89 import com.android.gallery3d.filtershow.tools.SaveCopyTask;
90 import com.android.gallery3d.filtershow.tools.XmpPresets;
91 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
92 import com.android.gallery3d.filtershow.ui.FramedTextButton;
93 import com.android.gallery3d.filtershow.ui.Spline;
94 import com.android.gallery3d.util.GalleryUtils;
95 import com.android.gallery3d.util.UsageStatistics;
96 import com.android.photos.data.GalleryBitmapPool;
97
98 import java.io.File;
99 import java.lang.ref.WeakReference;
100 import java.util.Vector;
101
102 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
103         OnShareTargetSelectedListener {
104
105     private String mAction = "";
106     MasterImage mMasterImage = null;
107
108     private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
109
110     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
111     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
112     private ImageLoader mImageLoader = null;
113     private ImageShow mImageShow = null;
114
115     private View mSaveButton = null;
116
117     private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
118
119     private static final int SELECT_PICTURE = 1;
120     private static final String LOGTAG = "FilterShowActivity";
121     protected static final boolean ANIMATE_PANELS = true;
122
123     private boolean mShowingTinyPlanet = false;
124     private boolean mShowingImageStatePanel = false;
125
126     private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
127
128     private ShareActionProvider mShareActionProvider;
129     private File mSharedOutputFile = null;
130
131     private boolean mSharingImage = false;
132
133     private WeakReference<ProgressDialog> mSavingProgressDialog;
134
135     private LoadBitmapTask mLoadBitmapTask;
136     private boolean mLoading = true;
137
138     private Uri mOriginalImageUri = null;
139     private ImagePreset mOriginalPreset = null;
140
141     private CategoryAdapter mCategoryLooksAdapter = null;
142     private CategoryAdapter mCategoryBordersAdapter = null;
143     private CategoryAdapter mCategoryGeometryAdapter = null;
144     private CategoryAdapter mCategoryFiltersAdapter = null;
145     private int mCurrentPanel = MainPanel.LOOKS;
146
147     @Override
148     public void onCreate(Bundle savedInstanceState) {
149         super.onCreate(savedInstanceState);
150
151         boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
152         if (onlyUsePortrait) {
153             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
154         }
155         MasterImage.setMaster(mMasterImage);
156
157         clearGalleryBitmapPool();
158
159         CachingPipeline.createRenderscriptContext(this);
160         setupMasterImage();
161         setDefaultValues();
162         fillEditors();
163
164         loadXML();
165         loadMainPanel();
166
167         setDefaultPreset();
168
169         extractXMPData();
170         processIntent();
171         UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
172         UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
173                 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
174     }
175
176     public boolean isShowingImageStatePanel() {
177         return mShowingImageStatePanel;
178     }
179
180     public void loadMainPanel() {
181         if (findViewById(R.id.main_panel_container) == null) {
182             return;
183         }
184         MainPanel panel = new MainPanel();
185         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
186         transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
187         transaction.commit();
188     }
189
190     public void loadEditorPanel(FilterRepresentation representation,
191                                 final Editor currentEditor) {
192         if (representation.getEditorId() == ImageOnlyEditor.ID) {
193             currentEditor.reflectCurrentFilter();
194             return;
195         }
196         final int currentId = currentEditor.getID();
197         Runnable showEditor = new Runnable() {
198             @Override
199             public void run() {
200                 EditorPanel panel = new EditorPanel();
201                 panel.setEditor(currentId);
202                 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
203                 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
204                 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
205                 transaction.commit();
206             }
207         };
208         Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
209         boolean doAnimation = false;
210         if (mShowingImageStatePanel
211                 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
212             doAnimation = true;
213         }
214         if (doAnimation && main != null && main instanceof MainPanel) {
215             MainPanel mainPanel = (MainPanel) main;
216             View container = mainPanel.getView().findViewById(R.id.category_panel_container);
217             View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
218             int panelHeight = container.getHeight() + bottom.getHeight();
219             ViewPropertyAnimator anim = mainPanel.getView().animate();
220             anim.translationY(panelHeight).start();
221             final Handler handler = new Handler();
222             handler.postDelayed(showEditor, anim.getDuration());
223         } else {
224             showEditor.run();
225         }
226     }
227
228     private void loadXML() {
229         setContentView(R.layout.filtershow_activity);
230
231         ActionBar actionBar = getActionBar();
232         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
233         actionBar.setCustomView(R.layout.filtershow_actionbar);
234
235         mSaveButton = actionBar.getCustomView();
236         mSaveButton.setOnClickListener(new OnClickListener() {
237             @Override
238             public void onClick(View view) {
239                 saveImage();
240             }
241         });
242
243         mImageShow = (ImageShow) findViewById(R.id.imageShow);
244         mImageViews.add(mImageShow);
245
246         setupEditors();
247
248         mEditorPlaceHolder.hide();
249
250         mImageShow.setImageLoader(mImageLoader);
251
252         fillFx();
253         fillBorders();
254         fillGeometry();
255         fillFilters();
256
257         setupStatePanel();
258     }
259
260     public void setupStatePanel() {
261         mImageLoader.setAdapter(mMasterImage.getHistory());
262     }
263
264     private void fillFilters() {
265         Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
266         FiltersManager filtersManager = FiltersManager.getManager();
267         filtersManager.addEffects(filtersRepresentations);
268
269         mCategoryFiltersAdapter = new CategoryAdapter(this);
270         for (FilterRepresentation representation : filtersRepresentations) {
271             if (representation.getTextId() != 0) {
272                 representation.setName(getString(representation.getTextId()));
273             }
274             mCategoryFiltersAdapter.add(new Action(this, representation));
275         }
276     }
277
278     private void fillGeometry() {
279         Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
280         FiltersManager filtersManager = FiltersManager.getManager();
281
282         GeometryMetadata geo = new GeometryMetadata();
283         int[] editorsId = geo.getEditorIds();
284         for (int i = 0; i < editorsId.length; i++) {
285             int editorId = editorsId[i];
286             GeometryMetadata geometry = new GeometryMetadata(geo);
287             geometry.setEditorId(editorId);
288             EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId);
289             geometry.setTextId(editorInfo.getTextId());
290             geometry.setOverlayId(editorInfo.getOverlayId());
291             geometry.setOverlayOnly(editorInfo.getOverlayOnly());
292             if (geometry.getTextId() != 0) {
293                 geometry.setName(getString(geometry.getTextId()));
294             }
295             filtersRepresentations.add(geometry);
296         }
297
298         filtersManager.addTools(filtersRepresentations);
299
300         mCategoryGeometryAdapter = new CategoryAdapter(this);
301         for (FilterRepresentation representation : filtersRepresentations) {
302             mCategoryGeometryAdapter.add(new Action(this, representation));
303         }
304     }
305
306     private void processIntent() {
307         Intent intent = getIntent();
308         if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
309             getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
310         }
311
312         mAction = intent.getAction();
313         Uri srcUri = intent.getData();
314         if (mOriginalImageUri != null) {
315             srcUri = mOriginalImageUri;
316         }
317         if (srcUri != null) {
318             startLoadBitmap(srcUri);
319         } else {
320             pickImage();
321         }
322     }
323
324     private void setupEditors() {
325         mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
326         EditorManager.addEditors(mEditorPlaceHolder);
327         mEditorPlaceHolder.setOldViews(mImageViews);
328         mEditorPlaceHolder.setImageLoader(mImageLoader);
329     }
330
331     private void fillEditors() {
332         mEditorPlaceHolder.addEditor(new EditorDraw());
333         mEditorPlaceHolder.addEditor(new BasicEditor());
334         mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
335         mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
336         mEditorPlaceHolder.addEditor(new EditorRedEye());
337         mEditorPlaceHolder.addEditor(new EditorCrop());
338         mEditorPlaceHolder.addEditor(new EditorFlip());
339         mEditorPlaceHolder.addEditor(new EditorRotate());
340         mEditorPlaceHolder.addEditor(new EditorStraighten());
341     }
342
343     private void setDefaultValues() {
344         ImageFilter.setActivityForMemoryToasts(this);
345
346         Resources res = getResources();
347         FiltersManager.setResources(res);
348
349         CategoryView.setMargin((int) getPixelsFromDip(8));
350         CategoryView.setTextSize((int) getPixelsFromDip(16));
351
352         // TODO: get those values from XML.
353         FramedTextButton.setTextSize((int) getPixelsFromDip(14));
354         FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
355         FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
356
357         Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
358         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
359         Spline.setCurveHandle(curveHandle, curveHandleSize);
360         Spline.setCurveWidth((int) getPixelsFromDip(3));
361
362         ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
363         ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
364         ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
365     }
366
367     private void startLoadBitmap(Uri uri) {
368         mLoading = true;
369         final View loading = findViewById(R.id.loading);
370         final View imageShow = findViewById(R.id.imageShow);
371         imageShow.setVisibility(View.INVISIBLE);
372         loading.setVisibility(View.VISIBLE);
373         mShowingTinyPlanet = false;
374         mLoadBitmapTask = new LoadBitmapTask();
375         mLoadBitmapTask.execute(uri);
376     }
377
378     private void fillBorders() {
379         Vector<FilterRepresentation> borders = new Vector<FilterRepresentation>();
380
381         // The "no border" implementation
382         borders.add(new FilterImageBorderRepresentation(0));
383
384         // Google-build borders
385         FiltersManager.getManager().addBorders(this, borders);
386
387         for (int i = 0; i < borders.size(); i++) {
388             FilterRepresentation filter = borders.elementAt(i);
389             filter.setName(getString(R.string.borders));
390             if (i == 0) {
391                 filter.setName(getString(R.string.none));
392             }
393         }
394
395         mCategoryBordersAdapter = new CategoryAdapter(this);
396         for (FilterRepresentation representation : borders) {
397             if (representation.getTextId() != 0) {
398                 representation.setName(getString(representation.getTextId()));
399             }
400             mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
401         }
402     }
403
404     public CategoryAdapter getCategoryLooksAdapter() {
405         return mCategoryLooksAdapter;
406     }
407
408     public CategoryAdapter getCategoryBordersAdapter() {
409         return mCategoryBordersAdapter;
410     }
411
412     public CategoryAdapter getCategoryGeometryAdapter() {
413         return mCategoryGeometryAdapter;
414     }
415
416     public CategoryAdapter getCategoryFiltersAdapter() {
417         return mCategoryFiltersAdapter;
418     }
419
420     public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
421         if (filterRepresentation == null) {
422             return;
423         }
424         ImagePreset oldPreset = MasterImage.getImage().getPreset();
425         ImagePreset copy = new ImagePreset(oldPreset);
426         copy.removeFilter(filterRepresentation);
427         MasterImage.getImage().setPreset(copy, true);
428         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
429             FilterRepresentation lastRepresentation = copy.getLastRepresentation();
430             MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
431         }
432     }
433
434     public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
435         if (filterRepresentation == null) {
436             return;
437         }
438         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
439             return;
440         }
441         ImagePreset oldPreset = MasterImage.getImage().getPreset();
442         ImagePreset copy = new ImagePreset(oldPreset);
443         FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
444         if (representation == null) {
445             copy.addFilter(filterRepresentation);
446         } else {
447             if (filterRepresentation.allowsMultipleInstances()) {
448                 representation.updateTempParametersFrom(filterRepresentation);
449                 copy.setHistoryName(filterRepresentation.getName());
450                 representation.synchronizeRepresentation();
451             }
452             filterRepresentation = representation;
453         }
454         MasterImage.getImage().setPreset(copy, true);
455         MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
456     }
457
458     public void showRepresentation(FilterRepresentation representation) {
459         if (representation == null) {
460             return;
461         }
462         useFilterRepresentation(representation);
463
464         // show representation
465         Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
466         loadEditorPanel(representation, mCurrentEditor);
467     }
468
469     public Editor getEditor(int editorID) {
470         return mEditorPlaceHolder.getEditor(editorID);
471     }
472
473     public void setCurrentPanel(int currentPanel) {
474         mCurrentPanel = currentPanel;
475     }
476
477     public int getCurrentPanel() {
478         return mCurrentPanel;
479     }
480
481     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
482         int mBitmapSize;
483
484         public LoadBitmapTask() {
485             mBitmapSize = getScreenImageSize();
486         }
487
488         @Override
489         protected Boolean doInBackground(Uri... params) {
490             if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
491                 return false;
492             }
493             publishProgress(mImageLoader.queryLightCycle360());
494             return true;
495         }
496
497         @Override
498         protected void onProgressUpdate(Boolean... values) {
499             super.onProgressUpdate(values);
500             if (isCancelled()) {
501                 return;
502             }
503             if (values[0]) {
504                 mShowingTinyPlanet = true;
505             }
506         }
507
508         @Override
509         protected void onPostExecute(Boolean result) {
510             MasterImage.setMaster(mMasterImage);
511             if (isCancelled()) {
512                 return;
513             }
514
515             if (!result) {
516                 cannotLoadImage();
517             }
518
519             if (null == CachingPipeline.getRenderScriptContext()){
520                 Log.v(LOGTAG,"RenderScript context destroyed during load");
521                 return;
522             }
523             final View loading = findViewById(R.id.loading);
524             loading.setVisibility(View.GONE);
525             final View imageShow = findViewById(R.id.imageShow);
526             imageShow.setVisibility(View.VISIBLE);
527
528             Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
529             FilteringPipeline pipeline = FilteringPipeline.getPipeline();
530             pipeline.setOriginal(largeBitmap);
531             float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
532             pipeline.setPreviewScaleFactor(previewScale);
533             Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
534             if (highresBitmap != null) {
535                 float highResPreviewScale = (float) highresBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
536                 pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
537             }
538             if (!mShowingTinyPlanet) {
539                 mCategoryFiltersAdapter.removeTinyPlanet();
540             }
541             pipeline.turnOnPipeline(true);
542             MasterImage.getImage().setOriginalGeometry(largeBitmap);
543             mCategoryLooksAdapter.imageLoaded();
544             mCategoryBordersAdapter.imageLoaded();
545             mCategoryGeometryAdapter.imageLoaded();
546             mCategoryFiltersAdapter.imageLoaded();
547             mLoadBitmapTask = null;
548
549             if (mOriginalPreset != null) {
550                 MasterImage.getImage().setPreset(mOriginalPreset, true);
551                 mOriginalPreset = null;
552             }
553
554             if (mAction == TINY_PLANET_ACTION) {
555                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
556             }
557             mLoading = false;
558             super.onPostExecute(result);
559         }
560
561     }
562
563     private void clearGalleryBitmapPool() {
564         (new AsyncTask<Void, Void, Void>() {
565             @Override
566             protected Void doInBackground(Void... params) {
567                 // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
568                 GalleryBitmapPool.getInstance().clear();
569                 return null;
570             }
571         }).execute();
572     }
573
574     @Override
575     protected void onDestroy() {
576         if (mLoadBitmapTask != null) {
577             mLoadBitmapTask.cancel(false);
578         }
579         // TODO:  refactor, don't use so many singletons.
580         FilteringPipeline.getPipeline().turnOnPipeline(false);
581         MasterImage.reset();
582         FilteringPipeline.reset();
583         ImageFilter.resetStatics();
584         FiltersManager.getPreviewManager().freeRSFilterScripts();
585         FiltersManager.getManager().freeRSFilterScripts();
586         FiltersManager.getHighresManager().freeRSFilterScripts();
587         FiltersManager.reset();
588         CachingPipeline.destroyRenderScriptContext();
589         super.onDestroy();
590     }
591
592     private int getScreenImageSize() {
593         DisplayMetrics metrics = new DisplayMetrics();
594         Display display = getWindowManager().getDefaultDisplay();
595         Point size = new Point();
596         display.getSize(size);
597         display.getMetrics(metrics);
598         int msize = Math.min(size.x, size.y);
599         return (133 * msize) / metrics.densityDpi;
600     }
601
602     private void showSavingProgress(String albumName) {
603         ProgressDialog progress;
604         if (mSavingProgressDialog != null) {
605             progress = mSavingProgressDialog.get();
606             if (progress != null) {
607                 progress.show();
608                 return;
609             }
610         }
611         // TODO: Allow cancellation of the saving process
612         String progressText;
613         if (albumName == null) {
614             progressText = getString(R.string.saving_image);
615         } else {
616             progressText = getString(R.string.filtershow_saving_image, albumName);
617         }
618         progress = ProgressDialog.show(this, "", progressText, true, false);
619         mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
620     }
621
622     private void hideSavingProgress() {
623         if (mSavingProgressDialog != null) {
624             ProgressDialog progress = mSavingProgressDialog.get();
625             if (progress != null)
626                 progress.dismiss();
627         }
628     }
629
630     public void completeSaveImage(Uri saveUri) {
631         if (mSharingImage && mSharedOutputFile != null) {
632             // Image saved, we unblock the content provider
633             Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
634                     Uri.encode(mSharedOutputFile.getAbsolutePath()));
635             ContentValues values = new ContentValues();
636             values.put(SharedImageProvider.PREPARE, false);
637             getContentResolver().insert(uri, values);
638         }
639         setResult(RESULT_OK, new Intent().setData(saveUri));
640         hideSavingProgress();
641         finish();
642     }
643
644     @Override
645     public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
646         // First, let's tell the SharedImageProvider that it will need to wait
647         // for the image
648         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
649                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
650         ContentValues values = new ContentValues();
651         values.put(SharedImageProvider.PREPARE, true);
652         getContentResolver().insert(uri, values);
653         mSharingImage = true;
654
655         // Process and save the image in the background.
656         showSavingProgress(null);
657         mImageShow.saveImage(this, mSharedOutputFile);
658         return true;
659     }
660
661     private Intent getDefaultShareIntent() {
662         Intent intent = new Intent(Intent.ACTION_SEND);
663         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
664         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
665         intent.setType(SharedImageProvider.MIME_TYPE);
666         mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri());
667         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
668                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
669         intent.putExtra(Intent.EXTRA_STREAM, uri);
670         return intent;
671     }
672
673     @Override
674     public boolean onCreateOptionsMenu(Menu menu) {
675         getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
676         MenuItem showState = menu.findItem(R.id.showImageStateButton);
677         if (mShowingImageStatePanel) {
678             showState.setTitle(R.string.hide_imagestate_panel);
679         } else {
680             showState.setTitle(R.string.show_imagestate_panel);
681         }
682         mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
683                 .getActionProvider();
684         mShareActionProvider.setShareIntent(getDefaultShareIntent());
685         mShareActionProvider.setOnShareTargetSelectedListener(this);
686
687         MenuItem undoItem = menu.findItem(R.id.undoButton);
688         MenuItem redoItem = menu.findItem(R.id.redoButton);
689         MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
690         mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
691         return true;
692     }
693
694     @Override
695     public void onPause() {
696         super.onPause();
697         rsPause();
698         if (mShareActionProvider != null) {
699             mShareActionProvider.setOnShareTargetSelectedListener(null);
700         }
701     }
702
703     @Override
704     public void onResume() {
705         super.onResume();
706         rsResume();
707         if (mShareActionProvider != null) {
708             mShareActionProvider.setOnShareTargetSelectedListener(this);
709         }
710     }
711
712     private void rsResume() {
713         ImageFilter.setActivityForMemoryToasts(this);
714         MasterImage.setMaster(mMasterImage);
715         if (CachingPipeline.getRenderScriptContext() == null) {
716             CachingPipeline.createRenderscriptContext(this);
717         }
718         FiltersManager.setResources(getResources());
719         if (!mLoading) {
720             Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
721             FilteringPipeline pipeline = FilteringPipeline.getPipeline();
722             pipeline.setOriginal(largeBitmap);
723             float previewScale = (float) largeBitmap.getWidth() /
724                     (float) mImageLoader.getOriginalBounds().width();
725             pipeline.setPreviewScaleFactor(previewScale);
726             Bitmap highresBitmap = mImageLoader.getOriginalBitmapHighres();
727             if (highresBitmap != null) {
728                 float highResPreviewScale = (float) highresBitmap.getWidth() /
729                         (float) mImageLoader.getOriginalBounds().width();
730                 pipeline.setHighResPreviewScaleFactor(highResPreviewScale);
731             }
732             pipeline.turnOnPipeline(true);
733             MasterImage.getImage().setOriginalGeometry(largeBitmap);
734         }
735     }
736
737     private void rsPause() {
738         FilteringPipeline.getPipeline().turnOnPipeline(false);
739         FilteringPipeline.reset();
740         ImageFilter.resetStatics();
741         FiltersManager.getPreviewManager().freeRSFilterScripts();
742         FiltersManager.getManager().freeRSFilterScripts();
743         FiltersManager.getHighresManager().freeRSFilterScripts();
744         FiltersManager.reset();
745         CachingPipeline.destroyRenderScriptContext();
746     }
747
748     @Override
749     public boolean onOptionsItemSelected(MenuItem item) {
750         switch (item.getItemId()) {
751             case R.id.undoButton: {
752                 HistoryAdapter adapter = mMasterImage.getHistory();
753                 int position = adapter.undo();
754                 mMasterImage.onHistoryItemClick(position);
755                 backToMain();
756                 invalidateViews();
757                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
758                         UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
759                 return true;
760             }
761             case R.id.redoButton: {
762                 HistoryAdapter adapter = mMasterImage.getHistory();
763                 int position = adapter.redo();
764                 mMasterImage.onHistoryItemClick(position);
765                 invalidateViews();
766                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
767                         UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
768                 return true;
769             }
770             case R.id.resetHistoryButton: {
771                 resetHistory();
772                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
773                         UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
774                 return true;
775             }
776             case R.id.showImageStateButton: {
777                 toggleImageStatePanel();
778                 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
779                         UsageStatistics.CATEGORY_BUTTON_PRESS,
780                         mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
781                 return true;
782             }
783             case android.R.id.home: {
784                 saveImage();
785                 return true;
786             }
787         }
788         return false;
789     }
790
791     public void enableSave(boolean enable) {
792         if (mSaveButton != null)
793             mSaveButton.setEnabled(enable);
794     }
795
796     private void fillFx() {
797         FilterFxRepresentation nullFx =
798                 new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
799         Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
800         FiltersManager.getManager().addLooks(this, filtersRepresentations);
801
802         mCategoryLooksAdapter = new CategoryAdapter(this);
803         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
804         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
805         mCategoryLooksAdapter.add(new Action(this, nullFx, Action.FULL_VIEW));
806         for (FilterRepresentation representation : filtersRepresentations) {
807             mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
808         }
809     }
810
811     public void setDefaultPreset() {
812         // Default preset (original)
813         ImagePreset preset = new ImagePreset(getString(R.string.history_original)); // empty
814         preset.setImageLoader(mImageLoader);
815
816         mMasterImage.setPreset(preset, true);
817     }
818
819     // //////////////////////////////////////////////////////////////////////////////
820     // Some utility functions
821     // TODO: finish the cleanup.
822
823     public void invalidateViews() {
824         for (ImageShow views : mImageViews) {
825             views.invalidate();
826             views.updateImage();
827         }
828     }
829
830     public void hideImageViews() {
831         for (View view : mImageViews) {
832             view.setVisibility(View.GONE);
833         }
834         mEditorPlaceHolder.hide();
835     }
836
837     // //////////////////////////////////////////////////////////////////////////////
838     // imageState panel...
839
840     public void toggleImageStatePanel() {
841         invalidateOptionsMenu();
842         mShowingImageStatePanel = !mShowingImageStatePanel;
843         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
844         if (panel != null) {
845             if (panel instanceof EditorPanel) {
846                 EditorPanel editorPanel = (EditorPanel) panel;
847                 editorPanel.showImageStatePanel(mShowingImageStatePanel);
848             } else if (panel instanceof MainPanel) {
849                 MainPanel mainPanel = (MainPanel) panel;
850                 mainPanel.showImageStatePanel(mShowingImageStatePanel);
851             }
852         }
853     }
854
855     @Override
856     public void onConfigurationChanged(Configuration newConfig)
857     {
858         super.onConfigurationChanged(newConfig);
859         setDefaultValues();
860         loadXML();
861         loadMainPanel();
862
863         // mLoadBitmapTask==null implies you have looked at the intent
864         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
865             mCategoryFiltersAdapter.removeTinyPlanet();
866         }
867         final View loading = findViewById(R.id.loading);
868         loading.setVisibility(View.GONE);
869     }
870
871     public void setupMasterImage() {
872         mImageLoader = new ImageLoader(this, getApplicationContext());
873
874         HistoryAdapter mHistoryAdapter = new HistoryAdapter(
875                 this, R.layout.filtershow_history_operation_row,
876                 R.id.rowTextView);
877
878         StateAdapter mImageStateAdapter = new StateAdapter(this, 0);
879         MasterImage.reset();
880         mMasterImage = MasterImage.getImage();
881         mMasterImage.setHistoryAdapter(mHistoryAdapter);
882         mMasterImage.setStateAdapter(mImageStateAdapter);
883         mMasterImage.setActivity(this);
884         mMasterImage.setImageLoader(mImageLoader);
885
886         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
887             mMasterImage.setSupportsHighRes(true);
888         } else {
889             mMasterImage.setSupportsHighRes(false);
890         }
891     }
892
893     void resetHistory() {
894         HistoryAdapter adapter = mMasterImage.getHistory();
895         adapter.reset();
896         ImagePreset original = new ImagePreset(adapter.getItem(0));
897         mMasterImage.setPreset(original, true);
898         invalidateViews();
899         backToMain();
900     }
901
902     public void showDefaultImageView() {
903         mEditorPlaceHolder.hide();
904         mImageShow.setVisibility(View.VISIBLE);
905         MasterImage.getImage().setCurrentFilter(null);
906         MasterImage.getImage().setCurrentFilterRepresentation(null);
907     }
908
909     public void backToMain() {
910         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
911         if (currentPanel instanceof MainPanel) {
912             return;
913         }
914         loadMainPanel();
915         showDefaultImageView();
916     }
917
918     @Override
919     public void onBackPressed() {
920         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
921         if (currentPanel instanceof MainPanel) {
922             if (!mImageShow.hasModifications()) {
923                 done();
924             } else {
925                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
926                 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
927                 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
928                     public void onClick(DialogInterface dialog, int id) {
929                         saveImage();
930                     }
931                 });
932                 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
933                     public void onClick(DialogInterface dialog, int id) {
934                         done();
935                     }
936                 });
937                 builder.show();
938             }
939         } else {
940             backToMain();
941         }
942     }
943
944     public void cannotLoadImage() {
945         Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
946         finish();
947     }
948
949     // //////////////////////////////////////////////////////////////////////////////
950
951     public float getPixelsFromDip(float value) {
952         Resources r = getResources();
953         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
954                 r.getDisplayMetrics());
955     }
956
957     @Override
958     public void onItemClick(AdapterView<?> parent, View view, int position,
959             long id) {
960         mMasterImage.onHistoryItemClick(position);
961         invalidateViews();
962     }
963
964     public void pickImage() {
965         Intent intent = new Intent();
966         intent.setType("image/*");
967         intent.setAction(Intent.ACTION_GET_CONTENT);
968         startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
969                 SELECT_PICTURE);
970     }
971
972     @Override
973     public void onActivityResult(int requestCode, int resultCode, Intent data) {
974         if (resultCode == RESULT_OK) {
975             if (requestCode == SELECT_PICTURE) {
976                 Uri selectedImageUri = data.getData();
977                 startLoadBitmap(selectedImageUri);
978             }
979         }
980     }
981
982
983     public void saveImage() {
984         if (mImageShow.hasModifications()) {
985             // Get the name of the album, to which the image will be saved
986             File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
987             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
988             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
989             showSavingProgress(albumName);
990             mImageShow.saveImage(this, null);
991         } else {
992             done();
993         }
994     }
995
996
997     public void done() {
998         hideSavingProgress();
999         if (mLoadBitmapTask != null) {
1000             mLoadBitmapTask.cancel(false);
1001         }
1002         finish();
1003     }
1004
1005     static {
1006         System.loadLibrary("jni_filtershow_filters");
1007     }
1008
1009     private void extractXMPData() {
1010         XMresults res = XmpPresets.extractXMPData(
1011                 getBaseContext(), mMasterImage, getIntent().getData());
1012         if (res == null)
1013             return;
1014
1015         mOriginalImageUri = res.originalimage;
1016         mOriginalPreset = res.preset;
1017     }
1018 }