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             final View imageShow = findViewById(R.id.imageShow);
838             imageShow.setVisibility(View.VISIBLE);
839 
840 
841             Bitmap largeBitmap = PrimaryImage.getImage().getOriginalBitmapLarge();
842             mBoundService.setOriginalBitmap(largeBitmap);
843 
844             float previewScale = (float) largeBitmap.getWidth()
845                     / (float) PrimaryImage.getImage().getOriginalBounds().width();
846             mBoundService.setPreviewScaleFactor(previewScale);
847             if (!mShowingTinyPlanet) {
848                 mCategoryFiltersAdapter.removeTinyPlanet();
849             }
850             mCategoryLooksAdapter.imageLoaded();
851             mCategoryBordersAdapter.imageLoaded();
852             mCategoryGeometryAdapter.imageLoaded();
853             mCategoryFiltersAdapter.imageLoaded();
854             mLoadBitmapTask = null;
855 
856             PrimaryImage.getImage().warnListeners();
857             loadActions();
858 
859             if (mOriginalPreset != null) {
860                 PrimaryImage.getImage().setLoadedPreset(mOriginalPreset);
861                 PrimaryImage.getImage().setPreset(mOriginalPreset,
862                         mOriginalPreset.getLastRepresentation(), true);
863                 mOriginalPreset = null;
864             } else {
865                 setDefaultPreset();
866             }
867 
868             PrimaryImage.getImage().resetGeometryImages(true);
869 
870             if (mAction == TINY_PLANET_ACTION) {
871                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
872             }
873             LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
874             highresLoad.execute();
875             PrimaryImage.getImage().warnListeners();
876             super.onPostExecute(result);
877         }
878 
879     }
880 
clearGalleryBitmapPool()881     private void clearGalleryBitmapPool() {
882         (new AsyncTask<Void, Void, Void>() {
883             @Override
884             protected Void doInBackground(Void... params) {
885                 // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
886                 GalleryBitmapPool.getInstance().clear();
887                 return null;
888             }
889         }).execute();
890     }
891 
892     @Override
onDestroy()893     protected void onDestroy() {
894         if (mLoadBitmapTask != null) {
895             mLoadBitmapTask.cancel(false);
896         }
897         mUserPresetsManager.close();
898         doUnbindService();
899         super.onDestroy();
900     }
901 
902     // TODO: find a more robust way of handling image size selection
903     // for high screen densities.
getScreenImageSize()904     private int getScreenImageSize() {
905         DisplayMetrics outMetrics = new DisplayMetrics();
906         getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
907         return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
908     }
909 
showSavingProgress(String albumName)910     private void showSavingProgress(String albumName) {
911         ProgressDialog progress;
912         if (mSavingProgressDialog != null) {
913             progress = mSavingProgressDialog.get();
914             if (progress != null) {
915                 progress.show();
916                 return;
917             }
918         }
919         // TODO: Allow cancellation of the saving process
920         String progressText;
921         if (albumName == null) {
922             progressText = getString(R.string.saving_image);
923         } else {
924             progressText = getString(R.string.filtershow_saving_image, albumName);
925         }
926         progress = ProgressDialog.show(this, "", progressText, true, false);
927         mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
928     }
929 
hideSavingProgress()930     private void hideSavingProgress() {
931         if (mSavingProgressDialog != null) {
932             ProgressDialog progress = mSavingProgressDialog.get();
933             if (progress != null)
934                 progress.dismiss();
935         }
936     }
937 
completeSaveImage(Uri saveUri)938     public void completeSaveImage(Uri saveUri) {
939         if (mSharingImage && mSharedOutputFile != null) {
940             // Image saved, we unblock the content provider
941             Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
942                     Uri.encode(mSharedOutputFile.getAbsolutePath()));
943             ContentValues values = new ContentValues();
944             values.put(SharedImageProvider.PREPARE, false);
945             getContentResolver().insert(uri, values);
946         }
947         setResult(RESULT_OK, new Intent().setData(saveUri));
948         hideSavingProgress();
949         finish();
950     }
951 
952     @Override
onShareTargetSelected(ShareActionProvider arg0, Intent arg1)953     public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
954         // First, let's tell the SharedImageProvider that it will need to wait
955         // for the image
956         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
957                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
958         ContentValues values = new ContentValues();
959         values.put(SharedImageProvider.PREPARE, true);
960         getContentResolver().insert(uri, values);
961         mSharingImage = true;
962 
963         // Process and save the image in the background.
964         showSavingProgress(null);
965         mImageShow.saveImage(this, mSharedOutputFile);
966         return true;
967     }
968 
getDefaultShareIntent()969     private Intent getDefaultShareIntent() {
970         Intent intent = new Intent(Intent.ACTION_SEND);
971         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
972         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
973         intent.setType(SharedImageProvider.MIME_TYPE);
974         mSharedOutputFile = SaveImage.getNewFile(this, PrimaryImage.getImage().getUri());
975         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
976                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
977         intent.putExtra(Intent.EXTRA_STREAM, uri);
978         return intent;
979     }
980 
981     @Override
onCreateOptionsMenu(Menu menu)982     public boolean onCreateOptionsMenu(Menu menu) {
983         getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
984         MenuItem showState = menu.findItem(R.id.showImageStateButton);
985         if (mShowingImageStatePanel) {
986             showState.setTitle(R.string.hide_imagestate_panel);
987         } else {
988             showState.setTitle(R.string.show_imagestate_panel);
989         }
990         mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
991                 .getActionProvider();
992         mShareActionProvider.setShareIntent(getDefaultShareIntent());
993         mShareActionProvider.setOnShareTargetSelectedListener(this);
994         mMenu = menu;
995         setupMenu();
996         return true;
997     }
998 
setupMenu()999     private void setupMenu(){
1000         if (mMenu == null || mPrimaryImage == null) {
1001             return;
1002         }
1003         MenuItem undoItem = mMenu.findItem(R.id.undoButton);
1004         MenuItem redoItem = mMenu.findItem(R.id.redoButton);
1005         MenuItem resetItem = mMenu.findItem(R.id.resetHistoryButton);
1006         MenuItem printItem = mMenu.findItem(R.id.printButton);
1007         if (!PrintHelper.systemSupportsPrint()) {
1008             printItem.setVisible(false);
1009         }
1010         mPrimaryImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
1011     }
1012 
1013     @Override
onPause()1014     public void onPause() {
1015         super.onPause();
1016         if (mShareActionProvider != null) {
1017             mShareActionProvider.setOnShareTargetSelectedListener(null);
1018         }
1019     }
1020 
1021     @Override
onResume()1022     public void onResume() {
1023         super.onResume();
1024         if (mShareActionProvider != null) {
1025             mShareActionProvider.setOnShareTargetSelectedListener(this);
1026         }
1027     }
1028 
1029     @Override
onOptionsItemSelected(MenuItem item)1030     public boolean onOptionsItemSelected(MenuItem item) {
1031         switch (item.getItemId()) {
1032             case R.id.undoButton: {
1033                 HistoryManager adapter = mPrimaryImage.getHistory();
1034                 int position = adapter.undo();
1035                 mPrimaryImage.onHistoryItemClick(position);
1036                 backToMain();
1037                 invalidateViews();
1038                 return true;
1039             }
1040             case R.id.redoButton: {
1041                 HistoryManager adapter = mPrimaryImage.getHistory();
1042                 int position = adapter.redo();
1043                 mPrimaryImage.onHistoryItemClick(position);
1044                 invalidateViews();
1045                 return true;
1046             }
1047             case R.id.resetHistoryButton: {
1048                 resetHistory();
1049                 return true;
1050             }
1051             case R.id.showImageStateButton: {
1052                 toggleImageStatePanel();
1053                 return true;
1054             }
1055             case R.id.exportFlattenButton: {
1056                 showExportOptionsDialog();
1057                 return true;
1058             }
1059             case android.R.id.home: {
1060                 saveImage();
1061                 return true;
1062             }
1063             case R.id.manageUserPresets: {
1064                 manageUserPresets();
1065                 return true;
1066             }
1067             case R.id.showInfoPanel: {
1068                 toggleInformationPanel();
1069                 return true;
1070             }
1071             case R.id.printButton: {
1072                 print();
1073                 return true;
1074             }
1075         }
1076         return false;
1077     }
1078 
print()1079     public void print() {
1080         Bitmap bitmap = PrimaryImage.getImage().getHighresImage();
1081         PrintHelper printer = new PrintHelper(this);
1082         printer.printBitmap("ImagePrint", bitmap);
1083     }
1084 
addNewPreset()1085     public void addNewPreset() {
1086         DialogFragment dialog = new PresetManagementDialog();
1087         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1088     }
1089 
manageUserPresets()1090     private void manageUserPresets() {
1091         DialogFragment dialog = new PresetManagementDialog();
1092         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1093     }
1094 
showExportOptionsDialog()1095     private void showExportOptionsDialog() {
1096         DialogFragment dialog = new ExportDialog();
1097         dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
1098     }
1099 
updateUserPresetsFromAdapter(UserPresetsAdapter adapter)1100     public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
1101         ArrayList<FilterUserPresetRepresentation> representations =
1102                 adapter.getDeletedRepresentations();
1103         for (FilterUserPresetRepresentation representation : representations) {
1104             deletePreset(representation.getId());
1105         }
1106         ArrayList<FilterUserPresetRepresentation> changedRepresentations =
1107                 adapter.getChangedRepresentations();
1108         for (FilterUserPresetRepresentation representation : changedRepresentations) {
1109             updatePreset(representation);
1110         }
1111         adapter.clearDeletedRepresentations();
1112         adapter.clearChangedRepresentations();
1113         loadUserPresets();
1114     }
1115 
loadUserPresets()1116     public void loadUserPresets() {
1117         mUserPresetsManager.load();
1118         updateUserPresetsFromManager();
1119     }
1120 
updateUserPresetsFromManager()1121     public void updateUserPresetsFromManager() {
1122         ArrayList<FilterUserPresetRepresentation> presets =
1123                 mUserPresetsManager.getRepresentations();
1124         if (presets == null) {
1125             return;
1126         }
1127         if (mCategoryLooksAdapter != null) {
1128             fillLooks();
1129         }
1130         if (presets.size() > 0) {
1131             mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1132         }
1133         mUserPresetsAdapter.clear();
1134         for (int i = 0; i < presets.size(); i++) {
1135             FilterUserPresetRepresentation representation = presets.get(i);
1136             mCategoryLooksAdapter.add(
1137                     new Action(this, representation, Action.FULL_VIEW, true));
1138             mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1139         }
1140         if (presets.size() > 0) {
1141             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1142         }
1143         mCategoryLooksAdapter.notifyDataSetChanged();
1144         mCategoryLooksAdapter.notifyDataSetInvalidated();
1145     }
1146 
saveCurrentImagePreset(String name)1147     public void saveCurrentImagePreset(String name) {
1148         mUserPresetsManager.save(PrimaryImage.getImage().getPreset(), name);
1149     }
1150 
deletePreset(int id)1151     private void deletePreset(int id) {
1152         mUserPresetsManager.delete(id);
1153     }
1154 
updatePreset(FilterUserPresetRepresentation representation)1155     private void updatePreset(FilterUserPresetRepresentation representation) {
1156         mUserPresetsManager.update(representation);
1157     }
1158 
enableSave(boolean enable)1159     public void enableSave(boolean enable) {
1160         if (mSaveButton != null) {
1161             mSaveButton.setEnabled(enable);
1162         }
1163     }
1164 
fillLooks()1165     private void fillLooks() {
1166         FiltersManager filtersManager = FiltersManager.getManager();
1167         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1168 
1169         if (mCategoryLooksAdapter != null) {
1170             mCategoryLooksAdapter.clear();
1171         }
1172         mCategoryLooksAdapter = new CategoryAdapter(this);
1173         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1174         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1175         for (FilterRepresentation representation : filtersRepresentations) {
1176             mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1177         }
1178         if (mUserPresetsManager.getRepresentations() == null
1179             || mUserPresetsManager.getRepresentations().size() == 0) {
1180             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1181         }
1182 
1183         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1184         if (panel != null) {
1185             if (panel instanceof MainPanel) {
1186                 MainPanel mainPanel = (MainPanel) panel;
1187                 mainPanel.loadCategoryLookPanel(true);
1188             }
1189         }
1190     }
1191 
setDefaultPreset()1192     public void setDefaultPreset() {
1193         // Default preset (original)
1194         ImagePreset preset = new ImagePreset(); // empty
1195         mPrimaryImage.setPreset(preset, preset.getLastRepresentation(), true);
1196     }
1197 
1198     // //////////////////////////////////////////////////////////////////////////////
1199     // Some utility functions
1200     // TODO: finish the cleanup.
1201 
invalidateViews()1202     public void invalidateViews() {
1203         for (ImageShow views : mImageViews) {
1204             views.updateImage();
1205         }
1206     }
1207 
hideImageViews()1208     public void hideImageViews() {
1209         for (View view : mImageViews) {
1210             view.setVisibility(View.GONE);
1211         }
1212         mEditorPlaceHolder.hide();
1213     }
1214 
1215     // //////////////////////////////////////////////////////////////////////////////
1216     // imageState panel...
1217 
toggleImageStatePanel()1218     public void toggleImageStatePanel() {
1219         invalidateOptionsMenu();
1220         mShowingImageStatePanel = !mShowingImageStatePanel;
1221         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1222         if (panel != null) {
1223             if (panel instanceof EditorPanel) {
1224                 EditorPanel editorPanel = (EditorPanel) panel;
1225                 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1226             } else if (panel instanceof MainPanel) {
1227                 MainPanel mainPanel = (MainPanel) panel;
1228                 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1229             }
1230         }
1231     }
1232 
toggleVersionsPanel()1233     public void toggleVersionsPanel() {
1234         mShowingVersionsPanel = !mShowingVersionsPanel;
1235         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1236         if (panel != null && panel instanceof MainPanel) {
1237             MainPanel mainPanel = (MainPanel) panel;
1238             mainPanel.loadCategoryVersionsPanel();
1239         }
1240     }
1241 
1242     @Override
onConfigurationChanged(Configuration newConfig)1243     public void onConfigurationChanged(Configuration newConfig)
1244     {
1245         super.onConfigurationChanged(newConfig);
1246 
1247         setDefaultValues();
1248         if (mPrimaryImage == null) {
1249             return;
1250         }
1251         loadXML();
1252         fillCategories();
1253         loadMainPanel();
1254 
1255         if (mCurrentMenu != null) {
1256             mCurrentMenu.dismiss();
1257             mCurrentMenu = null;
1258         }
1259         if (mCurrentDialog != null) {
1260             mCurrentDialog.dismiss();
1261             mCurrentDialog = null;
1262         }
1263         // mLoadBitmapTask==null implies you have looked at the intent
1264         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1265             mCategoryFiltersAdapter.removeTinyPlanet();
1266         }
1267         stopLoadingIndicator();
1268     }
1269 
setupPrimaryImage()1270     public void setupPrimaryImage() {
1271 
1272         HistoryManager historyManager = new HistoryManager();
1273         StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1274         PrimaryImage.reset();
1275         mPrimaryImage = PrimaryImage.getImage();
1276         mPrimaryImage.setHistoryManager(historyManager);
1277         mPrimaryImage.setStateAdapter(imageStateAdapter);
1278         mPrimaryImage.setActivity(this);
1279 
1280         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1281             mPrimaryImage.setSupportsHighRes(true);
1282         } else {
1283             mPrimaryImage.setSupportsHighRes(false);
1284         }
1285     }
1286 
resetHistory()1287     void resetHistory() {
1288         HistoryManager adapter = mPrimaryImage.getHistory();
1289         adapter.reset();
1290         HistoryItem historyItem = adapter.getItem(0);
1291         ImagePreset original = null;
1292         if (RESET_TO_LOADED) {
1293             original = new ImagePreset(historyItem.getImagePreset());
1294         } else {
1295             original = new ImagePreset();
1296         }
1297         FilterRepresentation rep = null;
1298         if (historyItem != null) {
1299             rep = historyItem.getFilterRepresentation();
1300         }
1301         mPrimaryImage.setPreset(original, rep, true);
1302         invalidateViews();
1303         backToMain();
1304     }
1305 
showDefaultImageView()1306     public void showDefaultImageView() {
1307         mEditorPlaceHolder.hide();
1308         mImageShow.setVisibility(View.VISIBLE);
1309         PrimaryImage.getImage().setCurrentFilter(null);
1310         PrimaryImage.getImage().setCurrentFilterRepresentation(null);
1311     }
1312 
backToMain()1313     public void backToMain() {
1314         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(
1315                 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(
1326                 MainPanel.FRAGMENT_TAG);
1327         if (currentPanel instanceof MainPanel) {
1328             if (!mImageShow.hasModifications()) {
1329                 done();
1330             } else {
1331                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1332                 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1333                 builder.setPositiveButton(
1334                         R.string.save_and_exit,
1335                         new DialogInterface.OnClickListener() {
1336                     @Override
1337                     public void onClick(DialogInterface dialog, int id) {
1338                         saveImage();
1339                     }
1340                 });
1341                 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1342                     @Override
1343                     public void onClick(DialogInterface dialog, int id) {
1344                         done();
1345                     }
1346                 });
1347                 builder.show();
1348             }
1349         } else {
1350             backToMain();
1351         }
1352     }
1353 
cannotLoadImage()1354     public void cannotLoadImage() {
1355         Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1356         finish();
1357     }
1358 
1359     // //////////////////////////////////////////////////////////////////////////////
1360 
getPixelsFromDip(float value)1361     public float getPixelsFromDip(float value) {
1362         Resources r = getResources();
1363         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1364                 r.getDisplayMetrics());
1365     }
1366 
1367     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)1368     public void onItemClick(AdapterView<?> parent, View view, int position,
1369             long id) {
1370         mPrimaryImage.onHistoryItemClick(position);
1371         invalidateViews();
1372     }
1373 
pickImage()1374     public void pickImage() {
1375         Intent intent = new Intent();
1376         intent.setType("image/*");
1377         intent.setAction(Intent.ACTION_GET_CONTENT);
1378         startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1379                 SELECT_PICTURE);
1380     }
1381 
1382     @SuppressWarnings("MissingSuperCall") // TODO: Fix me
1383     @Override
onActivityResult(int requestCode, int resultCode, Intent data)1384     public void onActivityResult(int requestCode, int resultCode, Intent data) {
1385         if (resultCode == RESULT_OK) {
1386             if (requestCode == SELECT_PICTURE) {
1387                 Uri selectedImageUri = data.getData();
1388                 startLoadBitmap(selectedImageUri);
1389             }
1390         }
1391     }
1392 
1393 
saveImage()1394     public void saveImage() {
1395         if (mImageShow.hasModifications()) {
1396             // Get the name of the album, to which the image will be saved
1397             File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1398             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1399             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1400             showSavingProgress(albumName);
1401             mImageShow.saveImage(this, null);
1402         } else {
1403             done();
1404         }
1405     }
1406 
1407 
done()1408     public void done() {
1409         hideSavingProgress();
1410         if (mLoadBitmapTask != null) {
1411             mLoadBitmapTask.cancel(false);
1412         }
1413         finish();
1414     }
1415 
extractXMPData()1416     private void extractXMPData() {
1417         XMresults res = XmpPresets.extractXMPData(
1418                 getBaseContext(), mPrimaryImage, getIntent().getData());
1419         if (res == null)
1420             return;
1421 
1422         mOriginalImageUri = res.originalimage;
1423         mOriginalPreset = res.preset;
1424     }
1425 
getSelectedImageUri()1426     public Uri getSelectedImageUri() {
1427         return mSelectedImageUri;
1428     }
1429 
setHandlesSwipeForView(View view, float startX, float startY)1430     public void setHandlesSwipeForView(View view, float startX, float startY) {
1431         if (view != null) {
1432             mHandlingSwipeButton = true;
1433         } else {
1434             mHandlingSwipeButton = false;
1435         }
1436         mHandledSwipeView = view;
1437         int[] location = new int[2];
1438         view.getLocationInWindow(location);
1439         mSwipeStartX = location[0] + startX;
1440         mSwipeStartY = location[1] + startY;
1441     }
1442 
dispatchTouchEvent(MotionEvent ev)1443     public boolean dispatchTouchEvent (MotionEvent ev) {
1444         if (mHandlingSwipeButton) {
1445             int direction = CategoryView.HORIZONTAL;
1446             if (mHandledSwipeView instanceof CategoryView) {
1447                 direction = ((CategoryView) mHandledSwipeView).getOrientation();
1448             }
1449             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1450                 float delta = ev.getY() - mSwipeStartY;
1451                 float distance = mHandledSwipeView.getHeight();
1452                 if (direction == CategoryView.VERTICAL) {
1453                     delta = ev.getX() - mSwipeStartX;
1454                     mHandledSwipeView.setTranslationX(delta);
1455                     distance = mHandledSwipeView.getWidth();
1456                 } else {
1457                     mHandledSwipeView.setTranslationY(delta);
1458                 }
1459                 delta = Math.abs(delta);
1460                 float transparency = Math.min(1, delta / distance);
1461                 mHandledSwipeView.setAlpha(1.f - transparency);
1462                 mHandledSwipeViewLastDelta = delta;
1463             }
1464             if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1465                     || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1466                 mHandledSwipeView.setTranslationX(0);
1467                 mHandledSwipeView.setTranslationY(0);
1468                 mHandledSwipeView.setAlpha(1.f);
1469                 mHandlingSwipeButton = false;
1470                 float distance = mHandledSwipeView.getHeight();
1471                 if (direction == CategoryView.VERTICAL) {
1472                     distance = mHandledSwipeView.getWidth();
1473                 }
1474                 if (mHandledSwipeViewLastDelta > distance) {
1475                     ((SwipableView) mHandledSwipeView).delete();
1476                 }
1477             }
1478             return true;
1479         }
1480         return super.dispatchTouchEvent(ev);
1481     }
1482 
1483     public Point mHintTouchPoint = new Point();
1484 
hintTouchPoint(View view)1485     public Point hintTouchPoint(View view) {
1486         int location[] = new int[2];
1487         view.getLocationOnScreen(location);
1488         int x = mHintTouchPoint.x - location[0];
1489         int y = mHintTouchPoint.y - location[1];
1490         return new Point(x, y);
1491     }
1492 
startTouchAnimation(View target, float x, float y)1493     public void startTouchAnimation(View target, float x, float y) {
1494         final CategorySelected hint =
1495                 (CategorySelected) findViewById(R.id.categorySelectedIndicator);
1496         int location[] = new int[2];
1497         target.getLocationOnScreen(location);
1498         mHintTouchPoint.x = (int) (location[0] + x);
1499         mHintTouchPoint.y = (int) (location[1] + y);
1500         int locationHint[] = new int[2];
1501         ((View)hint.getParent()).getLocationOnScreen(locationHint);
1502         int dx = (int) (x - (hint.getWidth())/2);
1503         int dy = (int) (y - (hint.getHeight())/2);
1504         hint.setTranslationX(location[0] - locationHint[0] + dx);
1505         hint.setTranslationY(location[1] - locationHint[1] + dy);
1506         hint.setVisibility(View.VISIBLE);
1507         hint.animate().scaleX(2).scaleY(2).alpha(0).withEndAction(new Runnable() {
1508             @Override
1509             public void run() {
1510                 hint.setVisibility(View.INVISIBLE);
1511                 hint.setScaleX(1);
1512                 hint.setScaleY(1);
1513                 hint.setAlpha(1);
1514             }
1515         });
1516     }
1517 }
1518