OSDN Git Service

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