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.Color;
33 import android.graphics.Matrix;
34 import android.graphics.Point;
35 import android.graphics.Rect;
36 import android.graphics.RectF;
37 import android.graphics.drawable.ColorDrawable;
38 import android.graphics.drawable.Drawable;
39 import android.net.Uri;
40 import android.os.AsyncTask;
41 import android.os.Bundle;
42 import android.os.CancellationSignal;
43 import android.os.Handler;
44 import android.os.IBinder;
45 import androidx.fragment.app.DialogFragment;
46 import androidx.fragment.app.Fragment;
47 import androidx.fragment.app.FragmentActivity;
48 import androidx.fragment.app.FragmentTransaction;
49 import androidx.print.PrintHelper;
50 import android.util.DisplayMetrics;
51 import android.util.Log;
52 import android.util.TypedValue;
53 import android.view.Menu;
54 import android.view.MenuItem;
55 import android.view.MotionEvent;
56 import android.view.View;
57 import android.view.View.OnClickListener;
58 import android.view.ViewPropertyAnimator;
59 import android.view.WindowManager;
60 import android.widget.AdapterView;
61 import android.widget.AdapterView.OnItemClickListener;
62 import android.widget.FrameLayout;
63 import android.widget.PopupMenu;
64 import android.widget.ShareActionProvider;
65 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
66 import android.widget.Spinner;
67 import android.widget.Toast;
68 
69 import com.android.gallery3d.R;
70 import com.android.gallery3d.app.PhotoPage;
71 import com.android.gallery3d.data.LocalAlbum;
72 import com.android.gallery3d.filtershow.cache.ImageLoader;
73 import com.android.gallery3d.filtershow.category.Action;
74 import com.android.gallery3d.filtershow.category.CategoryAdapter;
75 import com.android.gallery3d.filtershow.category.CategorySelected;
76 import com.android.gallery3d.filtershow.category.CategoryView;
77 import com.android.gallery3d.filtershow.category.MainPanel;
78 import com.android.gallery3d.filtershow.category.SwipableView;
79 import com.android.gallery3d.filtershow.data.UserPresetsManager;
80 import com.android.gallery3d.filtershow.editors.BasicEditor;
81 import com.android.gallery3d.filtershow.editors.Editor;
82 import com.android.gallery3d.filtershow.editors.EditorColorBorder;
83 import com.android.gallery3d.filtershow.editors.EditorCrop;
84 import com.android.gallery3d.filtershow.editors.EditorDraw;
85 import com.android.gallery3d.filtershow.editors.EditorManager;
86 import com.android.gallery3d.filtershow.editors.EditorMirror;
87 import com.android.gallery3d.filtershow.editors.EditorPanel;
88 import com.android.gallery3d.filtershow.editors.EditorRedEye;
89 import com.android.gallery3d.filtershow.editors.EditorRotate;
90 import com.android.gallery3d.filtershow.editors.EditorStraighten;
91 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
92 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
93 import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation;
94 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
95 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
96 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
97 import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
98 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
99 import com.android.gallery3d.filtershow.filters.FiltersManager;
100 import com.android.gallery3d.filtershow.filters.ImageFilter;
101 import com.android.gallery3d.filtershow.history.HistoryItem;
102 import com.android.gallery3d.filtershow.history.HistoryManager;
103 import com.android.gallery3d.filtershow.imageshow.ImageShow;
104 import com.android.gallery3d.filtershow.imageshow.MasterImage;
105 import com.android.gallery3d.filtershow.imageshow.Spline;
106 import com.android.gallery3d.filtershow.info.InfoPanel;
107 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
108 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
109 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
110 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
111 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
112 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
113 import com.android.gallery3d.filtershow.state.StateAdapter;
114 import com.android.gallery3d.filtershow.tools.SaveImage;
115 import com.android.gallery3d.filtershow.tools.XmpPresets;
116 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
117 import com.android.gallery3d.filtershow.ui.ExportDialog;
118 import com.android.gallery3d.filtershow.ui.FramedTextButton;
119 import com.android.gallery3d.util.GalleryUtils;
120 import com.android.photos.data.GalleryBitmapPool;
121 
122 import java.io.File;
123 import java.io.FileDescriptor;
124 import java.io.FileOutputStream;
125 import java.lang.ref.WeakReference;
126 import java.util.ArrayList;
127 import java.util.Vector;
128 
129 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
130         OnShareTargetSelectedListener, DialogInterface.OnShowListener,
131         DialogInterface.OnDismissListener, PopupMenu.OnDismissListener{
132 
133     private String mAction = "";
134     MasterImage mMasterImage = null;
135 
136     private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
137 
138     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
139     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
140     public static final boolean RESET_TO_LOADED = false;
141     private ImageShow mImageShow = null;
142 
143     private View mSaveButton = null;
144 
145     private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
146     private Editor mCurrentEditor = null;
147 
148     private static final int SELECT_PICTURE = 1;
149     private static final String LOGTAG = "FilterShowActivity";
150 
151     private boolean mShowingTinyPlanet = false;
152     private boolean mShowingImageStatePanel = false;
153     private boolean mShowingVersionsPanel = false;
154 
155     private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
156 
157     private ShareActionProvider mShareActionProvider;
158     private File mSharedOutputFile = null;
159 
160     private boolean mSharingImage = false;
161 
162     private WeakReference<ProgressDialog> mSavingProgressDialog;
163 
164     private LoadBitmapTask mLoadBitmapTask;
165 
166     private Uri mOriginalImageUri = null;
167     private ImagePreset mOriginalPreset = null;
168 
169     private Uri mSelectedImageUri = null;
170 
171     private ArrayList<Action> mActions = new ArrayList<Action>();
172     private UserPresetsManager mUserPresetsManager = null;
173     private UserPresetsAdapter mUserPresetsAdapter = null;
174     private CategoryAdapter mCategoryLooksAdapter = null;
175     private CategoryAdapter mCategoryBordersAdapter = null;
176     private CategoryAdapter mCategoryGeometryAdapter = null;
177     private CategoryAdapter mCategoryFiltersAdapter = null;
178     private CategoryAdapter mCategoryVersionsAdapter = null;
179     private int mCurrentPanel = MainPanel.LOOKS;
180     private Vector<FilterUserPresetRepresentation> mVersions =
181             new Vector<FilterUserPresetRepresentation>();
182     private int mVersionsCounter = 0;
183 
184     private boolean mHandlingSwipeButton = false;
185     private View mHandledSwipeView = null;
186     private float mHandledSwipeViewLastDelta = 0;
187     private float mSwipeStartX = 0;
188     private float mSwipeStartY = 0;
189 
190     private ProcessingService mBoundService;
191     private boolean mIsBound = false;
192     private Menu mMenu;
193     private DialogInterface mCurrentDialog = null;
194     private PopupMenu mCurrentMenu = null;
195     private boolean mLoadingVisible = true;
196 
getProcessingService()197     public ProcessingService getProcessingService() {
198         return mBoundService;
199     }
200 
isSimpleEditAction()201     public boolean isSimpleEditAction() {
202         return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
203     }
204 
205     private ServiceConnection mConnection = new ServiceConnection() {
206         @Override
207         public void onServiceConnected(ComponentName className, IBinder service) {
208             /*
209              * This is called when the connection with the service has been
210              * established, giving us the service object we can use to
211              * interact with the service.  Because we have bound to a explicit
212              * service that we know is running in our own process, we can
213              * cast its IBinder to a concrete class and directly access it.
214              */
215             mBoundService = ((ProcessingService.LocalBinder)service).getService();
216             mBoundService.setFiltershowActivity(FilterShowActivity.this);
217             mBoundService.onStart();
218         }
219 
220         @Override
221         public void onServiceDisconnected(ComponentName className) {
222             /*
223              * This is called when the connection with the service has been
224              * unexpectedly disconnected -- that is, its process crashed.
225              * Because it is running in our same process, we should never
226              * see this happen.
227              */
228             mBoundService = null;
229         }
230     };
231 
doBindService()232     void doBindService() {
233         /*
234          * Establish a connection with the service.  We use an explicit
235          * class name because we want a specific service implementation that
236          * we know will be running in our own process (and thus won't be
237          * supporting component replacement by other applications).
238          */
239         bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
240                 mConnection, Context.BIND_AUTO_CREATE);
241         mIsBound = true;
242     }
243 
doUnbindService()244     void doUnbindService() {
245         if (mIsBound) {
246             // Detach our existing connection.
247             unbindService(mConnection);
248             mIsBound = false;
249         }
250     }
251 
updateUIAfterServiceStarted()252     public void updateUIAfterServiceStarted() {
253         MasterImage.setMaster(mMasterImage);
254         ImageFilter.setActivityForMemoryToasts(this);
255         mUserPresetsManager = new UserPresetsManager(this);
256         mUserPresetsAdapter = new UserPresetsAdapter(this);
257 
258         setupMasterImage();
259         setupMenu();
260         setDefaultValues();
261         fillEditors();
262         getWindow().setBackgroundDrawable(new ColorDrawable(0));
263         loadXML();
264 
265         fillCategories();
266         loadMainPanel();
267         extractXMPData();
268         processIntent();
269     }
270 
271     @Override
onCreate(Bundle savedInstanceState)272     public void onCreate(Bundle savedInstanceState) {
273         super.onCreate(savedInstanceState);
274 
275         boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
276         if (onlyUsePortrait) {
277             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
278         }
279 
280         clearGalleryBitmapPool();
281         doBindService();
282         getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));
283         setContentView(R.layout.filtershow_splashscreen);
284     }
285 
isShowingImageStatePanel()286     public boolean isShowingImageStatePanel() {
287         return mShowingImageStatePanel;
288     }
289 
loadMainPanel()290     public void loadMainPanel() {
291         if (findViewById(R.id.main_panel_container) == null) {
292             return;
293         }
294         MainPanel panel = new MainPanel();
295         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
296         transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
297         transaction.commitAllowingStateLoss();
298     }
299 
loadEditorPanel(FilterRepresentation representation, final Editor currentEditor)300     public void loadEditorPanel(FilterRepresentation representation,
301                                 final Editor currentEditor) {
302         if (representation.getEditorId() == ImageOnlyEditor.ID) {
303             currentEditor.reflectCurrentFilter();
304             return;
305         }
306         final int currentId = currentEditor.getID();
307         Runnable showEditor = new Runnable() {
308             @Override
309             public void run() {
310                 EditorPanel panel = new EditorPanel();
311                 panel.setEditor(currentId);
312                 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
313                 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
314                 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
315                 transaction.commit();
316             }
317         };
318         Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
319         boolean doAnimation = false;
320         if (mShowingImageStatePanel
321                 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
322             doAnimation = true;
323         }
324         if (doAnimation && main != null && main instanceof MainPanel) {
325             MainPanel mainPanel = (MainPanel) main;
326             View container = mainPanel.getView().findViewById(R.id.category_panel_container);
327             View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
328             int panelHeight = container.getHeight() + bottom.getHeight();
329             ViewPropertyAnimator anim = mainPanel.getView().animate();
330             anim.translationY(panelHeight).start();
331             final Handler handler = new Handler();
332             handler.postDelayed(showEditor, anim.getDuration());
333         } else {
334             showEditor.run();
335         }
336     }
337 
toggleInformationPanel()338     public void toggleInformationPanel() {
339         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
340         transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
341 
342         InfoPanel panel = new InfoPanel();
343         panel.show(transaction, InfoPanel.FRAGMENT_TAG);
344     }
345 
loadXML()346     private void loadXML() {
347         setContentView(R.layout.filtershow_activity);
348 
349         ActionBar actionBar = getActionBar();
350         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
351         actionBar.setCustomView(R.layout.filtershow_actionbar);
352         actionBar.setBackgroundDrawable(new ColorDrawable(
353                 getResources().getColor(R.color.background_screen)));
354 
355         mSaveButton = actionBar.getCustomView();
356         mSaveButton.setOnClickListener(new OnClickListener() {
357             @Override
358             public void onClick(View view) {
359                 saveImage();
360             }
361         });
362 
363         mImageShow = (ImageShow) findViewById(R.id.imageShow);
364         mImageViews.add(mImageShow);
365 
366         setupEditors();
367 
368         mEditorPlaceHolder.hide();
369         mImageShow.attach();
370 
371         setupStatePanel();
372     }
373 
fillCategories()374     public void fillCategories() {
375         fillLooks();
376         loadUserPresets();
377         fillBorders();
378         fillTools();
379         fillEffects();
380         fillVersions();
381     }
382 
setupStatePanel()383     public void setupStatePanel() {
384         MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
385     }
386 
fillVersions()387     private void fillVersions() {
388         if (mCategoryVersionsAdapter != null) {
389             mCategoryVersionsAdapter.clear();
390         }
391         mCategoryVersionsAdapter = new CategoryAdapter(this);
392         mCategoryVersionsAdapter.setShowAddButton(true);
393     }
394 
registerAction(Action action)395     public void registerAction(Action action) {
396         if (mActions.contains(action)) {
397             return;
398         }
399         mActions.add(action);
400     }
401 
loadActions()402     private void loadActions() {
403         for (int i = 0; i < mActions.size(); i++) {
404             Action action = mActions.get(i);
405             action.setImageFrame(new Rect(0, 0, 96, 96), 0);
406         }
407     }
408 
updateVersions()409     public void updateVersions() {
410         mCategoryVersionsAdapter.clear();
411         FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
412                 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
413         mCategoryVersionsAdapter.add(
414                 new Action(this, originalRep, Action.FULL_VIEW));
415         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
416         FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
417                 getString(R.string.filtershow_version_current), current, -1);
418         mCategoryVersionsAdapter.add(
419                 new Action(this, currentRep, Action.FULL_VIEW));
420         if (mVersions.size() > 0) {
421             mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
422         }
423         for (FilterUserPresetRepresentation rep : mVersions) {
424             mCategoryVersionsAdapter.add(
425                     new Action(this, rep, Action.FULL_VIEW, true));
426         }
427         mCategoryVersionsAdapter.notifyDataSetInvalidated();
428     }
429 
addCurrentVersion()430     public void addCurrentVersion() {
431         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
432         mVersionsCounter++;
433         FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
434                 "" + mVersionsCounter, current, -1);
435         mVersions.add(rep);
436         updateVersions();
437     }
438 
removeVersion(Action action)439     public void removeVersion(Action action) {
440         mVersions.remove(action.getRepresentation());
441         updateVersions();
442     }
443 
removeLook(Action action)444     public void removeLook(Action action) {
445         FilterUserPresetRepresentation rep =
446                 (FilterUserPresetRepresentation) action.getRepresentation();
447         if (rep == null) {
448             return;
449         }
450         mUserPresetsManager.delete(rep.getId());
451         updateUserPresetsFromManager();
452     }
453 
fillEffects()454     private void fillEffects() {
455         FiltersManager filtersManager = FiltersManager.getManager();
456         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
457         if (mCategoryFiltersAdapter != null) {
458             mCategoryFiltersAdapter.clear();
459         }
460         mCategoryFiltersAdapter = new CategoryAdapter(this);
461         for (FilterRepresentation representation : filtersRepresentations) {
462             if (representation.getTextId() != 0) {
463                 representation.setName(getString(representation.getTextId()));
464             }
465             mCategoryFiltersAdapter.add(new Action(this, representation));
466         }
467     }
468 
fillTools()469     private void fillTools() {
470         FiltersManager filtersManager = FiltersManager.getManager();
471         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
472         if (mCategoryGeometryAdapter != null) {
473             mCategoryGeometryAdapter.clear();
474         }
475         mCategoryGeometryAdapter = new CategoryAdapter(this);
476         boolean found = false;
477         for (FilterRepresentation representation : filtersRepresentations) {
478             mCategoryGeometryAdapter.add(new Action(this, representation));
479             if (representation instanceof FilterDrawRepresentation) {
480                 found = true;
481             }
482         }
483         if (!found) {
484             FilterRepresentation representation = new FilterDrawRepresentation();
485             Action action = new Action(this, representation);
486             action.setIsDoubleAction(true);
487             mCategoryGeometryAdapter.add(action);
488         }
489     }
490 
processIntent()491     private void processIntent() {
492         Intent intent = getIntent();
493         if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
494             getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
495         }
496 
497         mAction = intent.getAction();
498         mSelectedImageUri = intent.getData();
499         Uri loadUri = mSelectedImageUri;
500         if (mOriginalImageUri != null) {
501             loadUri = mOriginalImageUri;
502         }
503         if (loadUri != null) {
504             startLoadBitmap(loadUri);
505         } else {
506             pickImage();
507         }
508     }
509 
setupEditors()510     private void setupEditors() {
511         mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
512         EditorManager.addEditors(mEditorPlaceHolder);
513         mEditorPlaceHolder.setOldViews(mImageViews);
514     }
515 
fillEditors()516     private void fillEditors() {
517         mEditorPlaceHolder.addEditor(new EditorDraw());
518         mEditorPlaceHolder.addEditor(new EditorColorBorder());
519         mEditorPlaceHolder.addEditor(new BasicEditor());
520         mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
521         mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
522         mEditorPlaceHolder.addEditor(new EditorRedEye());
523         mEditorPlaceHolder.addEditor(new EditorCrop());
524         mEditorPlaceHolder.addEditor(new EditorMirror());
525         mEditorPlaceHolder.addEditor(new EditorRotate());
526         mEditorPlaceHolder.addEditor(new EditorStraighten());
527     }
528 
setDefaultValues()529     private void setDefaultValues() {
530         Resources res = getResources();
531 
532         // TODO: get those values from XML.
533         FramedTextButton.setTextSize((int) getPixelsFromDip(14));
534         FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
535         FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
536 
537         Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
538         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
539         Spline.setCurveHandle(curveHandle, curveHandleSize);
540         Spline.setCurveWidth((int) getPixelsFromDip(3));
541 
542         mOriginalImageUri = null;
543     }
544 
startLoadBitmap(Uri uri)545     private void startLoadBitmap(Uri uri) {
546         final View imageShow = findViewById(R.id.imageShow);
547         imageShow.setVisibility(View.INVISIBLE);
548         startLoadingIndicator();
549         mShowingTinyPlanet = false;
550         mLoadBitmapTask = new LoadBitmapTask();
551         mLoadBitmapTask.execute(uri);
552     }
553 
fillBorders()554     private void fillBorders() {
555         FiltersManager filtersManager = FiltersManager.getManager();
556         ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
557 
558         for (int i = 0; i < borders.size(); i++) {
559             FilterRepresentation filter = borders.get(i);
560             filter.setName(getString(R.string.borders));
561             if (i == 0) {
562                 filter.setName(getString(R.string.none));
563             }
564         }
565 
566         if (mCategoryBordersAdapter != null) {
567             mCategoryBordersAdapter.clear();
568         }
569         mCategoryBordersAdapter = new CategoryAdapter(this);
570         for (FilterRepresentation representation : borders) {
571             if (representation.getTextId() != 0) {
572                 representation.setName(getString(representation.getTextId()));
573             }
574             mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
575         }
576     }
577 
getUserPresetsAdapter()578     public UserPresetsAdapter getUserPresetsAdapter() {
579         return mUserPresetsAdapter;
580     }
581 
getCategoryLooksAdapter()582     public CategoryAdapter getCategoryLooksAdapter() {
583         return mCategoryLooksAdapter;
584     }
585 
getCategoryBordersAdapter()586     public CategoryAdapter getCategoryBordersAdapter() {
587         return mCategoryBordersAdapter;
588     }
589 
getCategoryGeometryAdapter()590     public CategoryAdapter getCategoryGeometryAdapter() {
591         return mCategoryGeometryAdapter;
592     }
593 
getCategoryFiltersAdapter()594     public CategoryAdapter getCategoryFiltersAdapter() {
595         return mCategoryFiltersAdapter;
596     }
597 
getCategoryVersionsAdapter()598     public CategoryAdapter getCategoryVersionsAdapter() {
599         return mCategoryVersionsAdapter;
600     }
601 
removeFilterRepresentation(FilterRepresentation filterRepresentation)602     public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
603         if (filterRepresentation == null) {
604             return;
605         }
606         ImagePreset oldPreset = MasterImage.getImage().getPreset();
607         ImagePreset copy = new ImagePreset(oldPreset);
608         copy.removeFilter(filterRepresentation);
609         MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
610         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
611             FilterRepresentation lastRepresentation = copy.getLastRepresentation();
612             MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
613         }
614     }
615 
useFilterRepresentation(FilterRepresentation filterRepresentation)616     public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
617         if (filterRepresentation == null) {
618             return;
619         }
620         if (!(filterRepresentation instanceof FilterRotateRepresentation)
621             && !(filterRepresentation instanceof FilterMirrorRepresentation)
622             && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
623             return;
624         }
625         if (filterRepresentation instanceof FilterUserPresetRepresentation
626                 || filterRepresentation instanceof FilterRotateRepresentation
627                 || filterRepresentation instanceof FilterMirrorRepresentation) {
628             MasterImage.getImage().onNewLook(filterRepresentation);
629         }
630         ImagePreset oldPreset = MasterImage.getImage().getPreset();
631         ImagePreset copy = new ImagePreset(oldPreset);
632         FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
633         if (representation == null) {
634             filterRepresentation = filterRepresentation.copy();
635             copy.addFilter(filterRepresentation);
636         } else {
637             if (filterRepresentation.allowsSingleInstanceOnly()) {
638                 // Don't just update the filter representation. Centralize the
639                 // logic in the addFilter(), such that we can keep "None" as
640                 // null.
641                 if (!representation.equals(filterRepresentation)) {
642                     // Only do this if the filter isn't the same
643                     // (state panel clicks can lead us here)
644                     copy.removeFilter(representation);
645                     copy.addFilter(filterRepresentation);
646                 }
647             }
648         }
649         MasterImage.getImage().setPreset(copy, filterRepresentation, true);
650         MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
651     }
652 
showRepresentation(FilterRepresentation representation)653     public void showRepresentation(FilterRepresentation representation) {
654         if (representation == null) {
655             return;
656         }
657 
658         if (representation instanceof FilterRotateRepresentation) {
659             FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
660             r.rotateCW();
661         }
662         if (representation instanceof FilterMirrorRepresentation) {
663             FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
664             r.cycle();
665         }
666         if (representation.isBooleanFilter()) {
667             ImagePreset preset = MasterImage.getImage().getPreset();
668             if (preset.getRepresentation(representation) != null) {
669                 // remove
670                 ImagePreset copy = new ImagePreset(preset);
671                 copy.removeFilter(representation);
672                 FilterRepresentation filterRepresentation = representation.copy();
673                 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
674                 MasterImage.getImage().setCurrentFilterRepresentation(null);
675                 return;
676             }
677         }
678         useFilterRepresentation(representation);
679 
680         // show representation
681         if (mCurrentEditor != null) {
682             mCurrentEditor.detach();
683         }
684         mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
685         loadEditorPanel(representation, mCurrentEditor);
686     }
687 
getEditor(int editorID)688     public Editor getEditor(int editorID) {
689         return mEditorPlaceHolder.getEditor(editorID);
690     }
691 
setCurrentPanel(int currentPanel)692     public void setCurrentPanel(int currentPanel) {
693         mCurrentPanel = currentPanel;
694     }
695 
getCurrentPanel()696     public int getCurrentPanel() {
697         return mCurrentPanel;
698     }
699 
updateCategories()700     public void updateCategories() {
701         if (mMasterImage == null) {
702             return;
703         }
704         ImagePreset preset = mMasterImage.getPreset();
705         mCategoryLooksAdapter.reflectImagePreset(preset);
706         mCategoryBordersAdapter.reflectImagePreset(preset);
707     }
708 
getMainStatePanelContainer(int id)709     public View getMainStatePanelContainer(int id) {
710         return findViewById(id);
711     }
712 
onShowMenu(PopupMenu menu)713     public void onShowMenu(PopupMenu menu) {
714         mCurrentMenu = menu;
715         menu.setOnDismissListener(this);
716     }
717 
718     @Override
onDismiss(PopupMenu popupMenu)719     public void onDismiss(PopupMenu popupMenu){
720         if (mCurrentMenu == null) {
721             return;
722         }
723         mCurrentMenu.setOnDismissListener(null);
724         mCurrentMenu = null;
725     }
726 
727     @Override
onShow(DialogInterface dialog)728     public void onShow(DialogInterface dialog) {
729         mCurrentDialog = dialog;
730     }
731 
732     @Override
onDismiss(DialogInterface dialogInterface)733     public void onDismiss(DialogInterface dialogInterface) {
734         mCurrentDialog = null;
735     }
736 
737     private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
738         @Override
doInBackground(Void... params)739         protected Boolean doInBackground(Void... params) {
740             MasterImage master = MasterImage.getImage();
741             Rect originalBounds = master.getOriginalBounds();
742             if (master.supportsHighRes()) {
743                 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
744                 if (highresPreviewSize > originalBounds.width()) {
745                     highresPreviewSize = originalBounds.width();
746                 }
747                 Rect bounds = new Rect();
748                 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
749                         master.getActivity(), highresPreviewSize,
750                         master.getOrientation(), bounds);
751                 master.setOriginalBounds(bounds);
752                 master.setOriginalBitmapHighres(originalHires);
753                 mBoundService.setOriginalBitmapHighres(originalHires);
754                 master.warnListeners();
755             }
756             return true;
757         }
758 
759         @Override
onPostExecute(Boolean result)760         protected void onPostExecute(Boolean result) {
761             Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
762             if (highresBitmap != null) {
763                 float highResPreviewScale = (float) highresBitmap.getWidth()
764                         / (float) MasterImage.getImage().getOriginalBounds().width();
765                 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
766             }
767             MasterImage.getImage().warnListeners();
768         }
769     }
770 
isLoadingVisible()771     public boolean isLoadingVisible() {
772         return mLoadingVisible;
773     }
774 
startLoadingIndicator()775     public void startLoadingIndicator() {
776         final View loading = findViewById(R.id.loading);
777         mLoadingVisible = true;
778         loading.setVisibility(View.VISIBLE);
779     }
780 
stopLoadingIndicator()781     public void stopLoadingIndicator() {
782         final View loading = findViewById(R.id.loading);
783         loading.setVisibility(View.GONE);
784         mLoadingVisible = false;
785     }
786 
787     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
788         int mBitmapSize;
789 
LoadBitmapTask()790         public LoadBitmapTask() {
791             mBitmapSize = getScreenImageSize();
792         }
793 
794         @Override
doInBackground(Uri... params)795         protected Boolean doInBackground(Uri... params) {
796             if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
797                 return false;
798             }
799             publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
800             return true;
801         }
802 
803         @Override
onProgressUpdate(Boolean... values)804         protected void onProgressUpdate(Boolean... values) {
805             super.onProgressUpdate(values);
806             if (isCancelled()) {
807                 return;
808             }
809             if (values[0]) {
810                 mShowingTinyPlanet = true;
811             }
812         }
813 
814         @Override
onPostExecute(Boolean result)815         protected void onPostExecute(Boolean result) {
816             MasterImage.setMaster(mMasterImage);
817             if (isCancelled()) {
818                 return;
819             }
820 
821             if (!result) {
822                 if (mOriginalImageUri != null
823                         && !mOriginalImageUri.equals(mSelectedImageUri)) {
824                     mOriginalImageUri = mSelectedImageUri;
825                     mOriginalPreset = null;
826                     Toast.makeText(FilterShowActivity.this,
827                             R.string.cannot_edit_original, Toast.LENGTH_SHORT).show();
828                     startLoadBitmap(mOriginalImageUri);
829                 } else {
830                     cannotLoadImage();
831                 }
832                 return;
833             }
834 
835             if (null == CachingPipeline.getRenderScriptContext()){
836                 Log.v(LOGTAG,"RenderScript context destroyed during load");
837                 return;
838             }
839             final View imageShow = findViewById(R.id.imageShow);
840             imageShow.setVisibility(View.VISIBLE);
841 
842 
843             Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
844             mBoundService.setOriginalBitmap(largeBitmap);
845 
846             float previewScale = (float) largeBitmap.getWidth()
847                     / (float) MasterImage.getImage().getOriginalBounds().width();
848             mBoundService.setPreviewScaleFactor(previewScale);
849             if (!mShowingTinyPlanet) {
850                 mCategoryFiltersAdapter.removeTinyPlanet();
851             }
852             mCategoryLooksAdapter.imageLoaded();
853             mCategoryBordersAdapter.imageLoaded();
854             mCategoryGeometryAdapter.imageLoaded();
855             mCategoryFiltersAdapter.imageLoaded();
856             mLoadBitmapTask = null;
857 
858             MasterImage.getImage().warnListeners();
859             loadActions();
860 
861             if (mOriginalPreset != null) {
862                 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
863                 MasterImage.getImage().setPreset(mOriginalPreset,
864                         mOriginalPreset.getLastRepresentation(), true);
865                 mOriginalPreset = null;
866             } else {
867                 setDefaultPreset();
868             }
869 
870             MasterImage.getImage().resetGeometryImages(true);
871 
872             if (mAction == TINY_PLANET_ACTION) {
873                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
874             }
875             LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
876             highresLoad.execute();
877             MasterImage.getImage().warnListeners();
878             super.onPostExecute(result);
879         }
880 
881     }
882 
clearGalleryBitmapPool()883     private void clearGalleryBitmapPool() {
884         (new AsyncTask<Void, Void, Void>() {
885             @Override
886             protected Void doInBackground(Void... params) {
887                 // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
888                 GalleryBitmapPool.getInstance().clear();
889                 return null;
890             }
891         }).execute();
892     }
893 
894     @Override
onDestroy()895     protected void onDestroy() {
896         if (mLoadBitmapTask != null) {
897             mLoadBitmapTask.cancel(false);
898         }
899         mUserPresetsManager.close();
900         doUnbindService();
901         super.onDestroy();
902     }
903 
904     // TODO: find a more robust way of handling image size selection
905     // for high screen densities.
getScreenImageSize()906     private int getScreenImageSize() {
907         DisplayMetrics outMetrics = new DisplayMetrics();
908         getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
909         return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
910     }
911 
showSavingProgress(String albumName)912     private void showSavingProgress(String albumName) {
913         ProgressDialog progress;
914         if (mSavingProgressDialog != null) {
915             progress = mSavingProgressDialog.get();
916             if (progress != null) {
917                 progress.show();
918                 return;
919             }
920         }
921         // TODO: Allow cancellation of the saving process
922         String progressText;
923         if (albumName == null) {
924             progressText = getString(R.string.saving_image);
925         } else {
926             progressText = getString(R.string.filtershow_saving_image, albumName);
927         }
928         progress = ProgressDialog.show(this, "", progressText, true, false);
929         mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
930     }
931 
hideSavingProgress()932     private void hideSavingProgress() {
933         if (mSavingProgressDialog != null) {
934             ProgressDialog progress = mSavingProgressDialog.get();
935             if (progress != null)
936                 progress.dismiss();
937         }
938     }
939 
completeSaveImage(Uri saveUri)940     public void completeSaveImage(Uri saveUri) {
941         if (mSharingImage && mSharedOutputFile != null) {
942             // Image saved, we unblock the content provider
943             Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
944                     Uri.encode(mSharedOutputFile.getAbsolutePath()));
945             ContentValues values = new ContentValues();
946             values.put(SharedImageProvider.PREPARE, false);
947             getContentResolver().insert(uri, values);
948         }
949         setResult(RESULT_OK, new Intent().setData(saveUri));
950         hideSavingProgress();
951         finish();
952     }
953 
954     @Override
onShareTargetSelected(ShareActionProvider arg0, Intent arg1)955     public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
956         // First, let's tell the SharedImageProvider that it will need to wait
957         // for the image
958         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
959                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
960         ContentValues values = new ContentValues();
961         values.put(SharedImageProvider.PREPARE, true);
962         getContentResolver().insert(uri, values);
963         mSharingImage = true;
964 
965         // Process and save the image in the background.
966         showSavingProgress(null);
967         mImageShow.saveImage(this, mSharedOutputFile);
968         return true;
969     }
970 
getDefaultShareIntent()971     private Intent getDefaultShareIntent() {
972         Intent intent = new Intent(Intent.ACTION_SEND);
973         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
974         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
975         intent.setType(SharedImageProvider.MIME_TYPE);
976         mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
977         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
978                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
979         intent.putExtra(Intent.EXTRA_STREAM, uri);
980         return intent;
981     }
982 
983     @Override
onCreateOptionsMenu(Menu menu)984     public boolean onCreateOptionsMenu(Menu menu) {
985         getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
986         MenuItem showState = menu.findItem(R.id.showImageStateButton);
987         if (mShowingImageStatePanel) {
988             showState.setTitle(R.string.hide_imagestate_panel);
989         } else {
990             showState.setTitle(R.string.show_imagestate_panel);
991         }
992         mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
993                 .getActionProvider();
994         mShareActionProvider.setShareIntent(getDefaultShareIntent());
995         mShareActionProvider.setOnShareTargetSelectedListener(this);
996         mMenu = menu;
997         setupMenu();
998         return true;
999     }
1000 
setupMenu()1001     private void setupMenu(){
1002         if (mMenu == null || mMasterImage == null) {
1003             return;
1004         }
1005         MenuItem undoItem = mMenu.findItem(R.id.undoButton);
1006         MenuItem redoItem = mMenu.findItem(R.id.redoButton);
1007         MenuItem resetItem = mMenu.findItem(R.id.resetHistoryButton);
1008         MenuItem printItem = mMenu.findItem(R.id.printButton);
1009         if (!PrintHelper.systemSupportsPrint()) {
1010             printItem.setVisible(false);
1011         }
1012         mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
1013     }
1014 
1015     @Override
onPause()1016     public void onPause() {
1017         super.onPause();
1018         if (mShareActionProvider != null) {
1019             mShareActionProvider.setOnShareTargetSelectedListener(null);
1020         }
1021     }
1022 
1023     @Override
onResume()1024     public void onResume() {
1025         super.onResume();
1026         if (mShareActionProvider != null) {
1027             mShareActionProvider.setOnShareTargetSelectedListener(this);
1028         }
1029     }
1030 
1031     @Override
onOptionsItemSelected(MenuItem item)1032     public boolean onOptionsItemSelected(MenuItem item) {
1033         switch (item.getItemId()) {
1034             case R.id.undoButton: {
1035                 HistoryManager adapter = mMasterImage.getHistory();
1036                 int position = adapter.undo();
1037                 mMasterImage.onHistoryItemClick(position);
1038                 backToMain();
1039                 invalidateViews();
1040                 return true;
1041             }
1042             case R.id.redoButton: {
1043                 HistoryManager adapter = mMasterImage.getHistory();
1044                 int position = adapter.redo();
1045                 mMasterImage.onHistoryItemClick(position);
1046                 invalidateViews();
1047                 return true;
1048             }
1049             case R.id.resetHistoryButton: {
1050                 resetHistory();
1051                 return true;
1052             }
1053             case R.id.showImageStateButton: {
1054                 toggleImageStatePanel();
1055                 return true;
1056             }
1057             case R.id.exportFlattenButton: {
1058                 showExportOptionsDialog();
1059                 return true;
1060             }
1061             case android.R.id.home: {
1062                 saveImage();
1063                 return true;
1064             }
1065             case R.id.manageUserPresets: {
1066                 manageUserPresets();
1067                 return true;
1068             }
1069             case R.id.showInfoPanel: {
1070                 toggleInformationPanel();
1071                 return true;
1072             }
1073             case R.id.printButton: {
1074                 print();
1075                 return true;
1076             }
1077         }
1078         return false;
1079     }
1080 
print()1081     public void print() {
1082         Bitmap bitmap = MasterImage.getImage().getHighresImage();
1083         PrintHelper printer = new PrintHelper(this);
1084         printer.printBitmap("ImagePrint", bitmap);
1085     }
1086 
addNewPreset()1087     public void addNewPreset() {
1088         DialogFragment dialog = new PresetManagementDialog();
1089         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1090     }
1091 
manageUserPresets()1092     private void manageUserPresets() {
1093         DialogFragment dialog = new PresetManagementDialog();
1094         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1095     }
1096 
showExportOptionsDialog()1097     private void showExportOptionsDialog() {
1098         DialogFragment dialog = new ExportDialog();
1099         dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
1100     }
1101 
updateUserPresetsFromAdapter(UserPresetsAdapter adapter)1102     public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
1103         ArrayList<FilterUserPresetRepresentation> representations =
1104                 adapter.getDeletedRepresentations();
1105         for (FilterUserPresetRepresentation representation : representations) {
1106             deletePreset(representation.getId());
1107         }
1108         ArrayList<FilterUserPresetRepresentation> changedRepresentations =
1109                 adapter.getChangedRepresentations();
1110         for (FilterUserPresetRepresentation representation : changedRepresentations) {
1111             updatePreset(representation);
1112         }
1113         adapter.clearDeletedRepresentations();
1114         adapter.clearChangedRepresentations();
1115         loadUserPresets();
1116     }
1117 
loadUserPresets()1118     public void loadUserPresets() {
1119         mUserPresetsManager.load();
1120         updateUserPresetsFromManager();
1121     }
1122 
updateUserPresetsFromManager()1123     public void updateUserPresetsFromManager() {
1124         ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1125         if (presets == null) {
1126             return;
1127         }
1128         if (mCategoryLooksAdapter != null) {
1129             fillLooks();
1130         }
1131         if (presets.size() > 0) {
1132             mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1133         }
1134         mUserPresetsAdapter.clear();
1135         for (int i = 0; i < presets.size(); i++) {
1136             FilterUserPresetRepresentation representation = presets.get(i);
1137             mCategoryLooksAdapter.add(
1138                     new Action(this, representation, Action.FULL_VIEW, true));
1139             mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1140         }
1141         if (presets.size() > 0) {
1142             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1143         }
1144         mCategoryLooksAdapter.notifyDataSetChanged();
1145         mCategoryLooksAdapter.notifyDataSetInvalidated();
1146     }
1147 
saveCurrentImagePreset(String name)1148     public void saveCurrentImagePreset(String name) {
1149         mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1150     }
1151 
deletePreset(int id)1152     private void deletePreset(int id) {
1153         mUserPresetsManager.delete(id);
1154     }
1155 
updatePreset(FilterUserPresetRepresentation representation)1156     private void updatePreset(FilterUserPresetRepresentation representation) {
1157         mUserPresetsManager.update(representation);
1158     }
1159 
enableSave(boolean enable)1160     public void enableSave(boolean enable) {
1161         if (mSaveButton != null) {
1162             mSaveButton.setEnabled(enable);
1163         }
1164     }
1165 
fillLooks()1166     private void fillLooks() {
1167         FiltersManager filtersManager = FiltersManager.getManager();
1168         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1169 
1170         if (mCategoryLooksAdapter != null) {
1171             mCategoryLooksAdapter.clear();
1172         }
1173         mCategoryLooksAdapter = new CategoryAdapter(this);
1174         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1175         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1176         for (FilterRepresentation representation : filtersRepresentations) {
1177             mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1178         }
1179         if (mUserPresetsManager.getRepresentations() == null
1180             || mUserPresetsManager.getRepresentations().size() == 0) {
1181             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1182         }
1183 
1184         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1185         if (panel != null) {
1186             if (panel instanceof MainPanel) {
1187                 MainPanel mainPanel = (MainPanel) panel;
1188                 mainPanel.loadCategoryLookPanel(true);
1189             }
1190         }
1191     }
1192 
setDefaultPreset()1193     public void setDefaultPreset() {
1194         // Default preset (original)
1195         ImagePreset preset = new ImagePreset(); // empty
1196         mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1197     }
1198 
1199     // //////////////////////////////////////////////////////////////////////////////
1200     // Some utility functions
1201     // TODO: finish the cleanup.
1202 
invalidateViews()1203     public void invalidateViews() {
1204         for (ImageShow views : mImageViews) {
1205             views.updateImage();
1206         }
1207     }
1208 
hideImageViews()1209     public void hideImageViews() {
1210         for (View view : mImageViews) {
1211             view.setVisibility(View.GONE);
1212         }
1213         mEditorPlaceHolder.hide();
1214     }
1215 
1216     // //////////////////////////////////////////////////////////////////////////////
1217     // imageState panel...
1218 
toggleImageStatePanel()1219     public void toggleImageStatePanel() {
1220         invalidateOptionsMenu();
1221         mShowingImageStatePanel = !mShowingImageStatePanel;
1222         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1223         if (panel != null) {
1224             if (panel instanceof EditorPanel) {
1225                 EditorPanel editorPanel = (EditorPanel) panel;
1226                 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1227             } else if (panel instanceof MainPanel) {
1228                 MainPanel mainPanel = (MainPanel) panel;
1229                 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1230             }
1231         }
1232     }
1233 
toggleVersionsPanel()1234     public void toggleVersionsPanel() {
1235         mShowingVersionsPanel = !mShowingVersionsPanel;
1236         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1237         if (panel != null && panel instanceof MainPanel) {
1238             MainPanel mainPanel = (MainPanel) panel;
1239             mainPanel.loadCategoryVersionsPanel();
1240         }
1241     }
1242 
1243     @Override
onConfigurationChanged(Configuration newConfig)1244     public void onConfigurationChanged(Configuration newConfig)
1245     {
1246         super.onConfigurationChanged(newConfig);
1247 
1248         setDefaultValues();
1249         if (mMasterImage == null) {
1250             return;
1251         }
1252         loadXML();
1253         fillCategories();
1254         loadMainPanel();
1255 
1256         if (mCurrentMenu != null) {
1257             mCurrentMenu.dismiss();
1258             mCurrentMenu = null;
1259         }
1260         if (mCurrentDialog != null) {
1261             mCurrentDialog.dismiss();
1262             mCurrentDialog = null;
1263         }
1264         // mLoadBitmapTask==null implies you have looked at the intent
1265         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1266             mCategoryFiltersAdapter.removeTinyPlanet();
1267         }
1268         stopLoadingIndicator();
1269     }
1270 
setupMasterImage()1271     public void setupMasterImage() {
1272 
1273         HistoryManager historyManager = new HistoryManager();
1274         StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1275         MasterImage.reset();
1276         mMasterImage = MasterImage.getImage();
1277         mMasterImage.setHistoryManager(historyManager);
1278         mMasterImage.setStateAdapter(imageStateAdapter);
1279         mMasterImage.setActivity(this);
1280 
1281         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1282             mMasterImage.setSupportsHighRes(true);
1283         } else {
1284             mMasterImage.setSupportsHighRes(false);
1285         }
1286     }
1287 
resetHistory()1288     void resetHistory() {
1289         HistoryManager adapter = mMasterImage.getHistory();
1290         adapter.reset();
1291         HistoryItem historyItem = adapter.getItem(0);
1292         ImagePreset original = null;
1293         if (RESET_TO_LOADED) {
1294             original = new ImagePreset(historyItem.getImagePreset());
1295         } else {
1296             original = new ImagePreset();
1297         }
1298         FilterRepresentation rep = null;
1299         if (historyItem != null) {
1300             rep = historyItem.getFilterRepresentation();
1301         }
1302         mMasterImage.setPreset(original, rep, true);
1303         invalidateViews();
1304         backToMain();
1305     }
1306 
showDefaultImageView()1307     public void showDefaultImageView() {
1308         mEditorPlaceHolder.hide();
1309         mImageShow.setVisibility(View.VISIBLE);
1310         MasterImage.getImage().setCurrentFilter(null);
1311         MasterImage.getImage().setCurrentFilterRepresentation(null);
1312     }
1313 
backToMain()1314     public void backToMain() {
1315         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1316         if (currentPanel instanceof MainPanel) {
1317             return;
1318         }
1319         loadMainPanel();
1320         showDefaultImageView();
1321     }
1322 
1323     @Override
onBackPressed()1324     public void onBackPressed() {
1325         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1326         if (currentPanel instanceof MainPanel) {
1327             if (!mImageShow.hasModifications()) {
1328                 done();
1329             } else {
1330                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1331                 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1332                 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1333                     @Override
1334                     public void onClick(DialogInterface dialog, int id) {
1335                         saveImage();
1336                     }
1337                 });
1338                 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1339                     @Override
1340                     public void onClick(DialogInterface dialog, int id) {
1341                         done();
1342                     }
1343                 });
1344                 builder.show();
1345             }
1346         } else {
1347             backToMain();
1348         }
1349     }
1350 
cannotLoadImage()1351     public void cannotLoadImage() {
1352         Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1353         finish();
1354     }
1355 
1356     // //////////////////////////////////////////////////////////////////////////////
1357 
getPixelsFromDip(float value)1358     public float getPixelsFromDip(float value) {
1359         Resources r = getResources();
1360         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1361                 r.getDisplayMetrics());
1362     }
1363 
1364     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)1365     public void onItemClick(AdapterView<?> parent, View view, int position,
1366             long id) {
1367         mMasterImage.onHistoryItemClick(position);
1368         invalidateViews();
1369     }
1370 
pickImage()1371     public void pickImage() {
1372         Intent intent = new Intent();
1373         intent.setType("image/*");
1374         intent.setAction(Intent.ACTION_GET_CONTENT);
1375         startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1376                 SELECT_PICTURE);
1377     }
1378 
1379     @Override
onActivityResult(int requestCode, int resultCode, Intent data)1380     public void onActivityResult(int requestCode, int resultCode, Intent data) {
1381         if (resultCode == RESULT_OK) {
1382             if (requestCode == SELECT_PICTURE) {
1383                 Uri selectedImageUri = data.getData();
1384                 startLoadBitmap(selectedImageUri);
1385             }
1386         }
1387     }
1388 
1389 
saveImage()1390     public void saveImage() {
1391         if (mImageShow.hasModifications()) {
1392             // Get the name of the album, to which the image will be saved
1393             File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1394             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1395             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1396             showSavingProgress(albumName);
1397             mImageShow.saveImage(this, null);
1398         } else {
1399             done();
1400         }
1401     }
1402 
1403 
done()1404     public void done() {
1405         hideSavingProgress();
1406         if (mLoadBitmapTask != null) {
1407             mLoadBitmapTask.cancel(false);
1408         }
1409         finish();
1410     }
1411 
extractXMPData()1412     private void extractXMPData() {
1413         XMresults res = XmpPresets.extractXMPData(
1414                 getBaseContext(), mMasterImage, getIntent().getData());
1415         if (res == null)
1416             return;
1417 
1418         mOriginalImageUri = res.originalimage;
1419         mOriginalPreset = res.preset;
1420     }
1421 
getSelectedImageUri()1422     public Uri getSelectedImageUri() {
1423         return mSelectedImageUri;
1424     }
1425 
setHandlesSwipeForView(View view, float startX, float startY)1426     public void setHandlesSwipeForView(View view, float startX, float startY) {
1427         if (view != null) {
1428             mHandlingSwipeButton = true;
1429         } else {
1430             mHandlingSwipeButton = false;
1431         }
1432         mHandledSwipeView = view;
1433         int[] location = new int[2];
1434         view.getLocationInWindow(location);
1435         mSwipeStartX = location[0] + startX;
1436         mSwipeStartY = location[1] + startY;
1437     }
1438 
dispatchTouchEvent(MotionEvent ev)1439     public boolean dispatchTouchEvent (MotionEvent ev) {
1440         if (mHandlingSwipeButton) {
1441             int direction = CategoryView.HORIZONTAL;
1442             if (mHandledSwipeView instanceof CategoryView) {
1443                 direction = ((CategoryView) mHandledSwipeView).getOrientation();
1444             }
1445             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1446                 float delta = ev.getY() - mSwipeStartY;
1447                 float distance = mHandledSwipeView.getHeight();
1448                 if (direction == CategoryView.VERTICAL) {
1449                     delta = ev.getX() - mSwipeStartX;
1450                     mHandledSwipeView.setTranslationX(delta);
1451                     distance = mHandledSwipeView.getWidth();
1452                 } else {
1453                     mHandledSwipeView.setTranslationY(delta);
1454                 }
1455                 delta = Math.abs(delta);
1456                 float transparency = Math.min(1, delta / distance);
1457                 mHandledSwipeView.setAlpha(1.f - transparency);
1458                 mHandledSwipeViewLastDelta = delta;
1459             }
1460             if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1461                     || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1462                 mHandledSwipeView.setTranslationX(0);
1463                 mHandledSwipeView.setTranslationY(0);
1464                 mHandledSwipeView.setAlpha(1.f);
1465                 mHandlingSwipeButton = false;
1466                 float distance = mHandledSwipeView.getHeight();
1467                 if (direction == CategoryView.VERTICAL) {
1468                     distance = mHandledSwipeView.getWidth();
1469                 }
1470                 if (mHandledSwipeViewLastDelta > distance) {
1471                     ((SwipableView) mHandledSwipeView).delete();
1472                 }
1473             }
1474             return true;
1475         }
1476         return super.dispatchTouchEvent(ev);
1477     }
1478 
1479     public Point mHintTouchPoint = new Point();
1480 
hintTouchPoint(View view)1481     public Point hintTouchPoint(View view) {
1482         int location[] = new int[2];
1483         view.getLocationOnScreen(location);
1484         int x = mHintTouchPoint.x - location[0];
1485         int y = mHintTouchPoint.y - location[1];
1486         return new Point(x, y);
1487     }
1488 
startTouchAnimation(View target, float x, float y)1489     public void startTouchAnimation(View target, float x, float y) {
1490         final CategorySelected hint =
1491                 (CategorySelected) findViewById(R.id.categorySelectedIndicator);
1492         int location[] = new int[2];
1493         target.getLocationOnScreen(location);
1494         mHintTouchPoint.x = (int) (location[0] + x);
1495         mHintTouchPoint.y = (int) (location[1] + y);
1496         int locationHint[] = new int[2];
1497         ((View)hint.getParent()).getLocationOnScreen(locationHint);
1498         int dx = (int) (x - (hint.getWidth())/2);
1499         int dy = (int) (y - (hint.getHeight())/2);
1500         hint.setTranslationX(location[0] - locationHint[0] + dx);
1501         hint.setTranslationY(location[1] - locationHint[1] + dy);
1502         hint.setVisibility(View.VISIBLE);
1503         hint.animate().scaleX(2).scaleY(2).alpha(0).withEndAction(new Runnable() {
1504             @Override
1505             public void run() {
1506                 hint.setVisibility(View.INVISIBLE);
1507                 hint.setScaleX(1);
1508                 hint.setScaleY(1);
1509                 hint.setAlpha(1);
1510             }
1511         });
1512     }
1513 }
1514