OSDN Git Service

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