OSDN Git Service

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