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