2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.filtershow;
19 import android.app.ActionBar;
20 import android.app.AlertDialog;
21 import android.app.ProgressDialog;
22 import android.content.ComponentName;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.ServiceConnection;
28 import android.content.pm.ActivityInfo;
29 import android.content.res.Configuration;
30 import android.content.res.Resources;
31 import android.graphics.Bitmap;
32 import android.graphics.Rect;
33 import android.graphics.drawable.Drawable;
34 import android.net.Uri;
35 import android.os.AsyncTask;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.support.v4.app.DialogFragment;
40 import android.support.v4.app.Fragment;
41 import android.support.v4.app.FragmentActivity;
42 import android.support.v4.app.FragmentTransaction;
43 import android.util.DisplayMetrics;
44 import android.util.Log;
45 import android.util.TypedValue;
46 import android.view.Menu;
47 import android.view.MenuItem;
48 import android.view.MotionEvent;
49 import android.view.View;
50 import android.view.View.OnClickListener;
51 import android.view.ViewPropertyAnimator;
52 import android.view.WindowManager;
53 import android.widget.AdapterView;
54 import android.widget.AdapterView.OnItemClickListener;
55 import android.widget.FrameLayout;
56 import android.widget.ShareActionProvider;
57 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
58 import android.widget.Toast;
60 import com.android.gallery3d.R;
61 import com.android.gallery3d.app.PhotoPage;
62 import com.android.gallery3d.data.LocalAlbum;
63 import com.android.gallery3d.filtershow.cache.ImageLoader;
64 import com.android.gallery3d.filtershow.category.Action;
65 import com.android.gallery3d.filtershow.category.CategoryAdapter;
66 import com.android.gallery3d.filtershow.category.MainPanel;
67 import com.android.gallery3d.filtershow.category.SwipableView;
68 import com.android.gallery3d.filtershow.data.UserPresetsManager;
69 import com.android.gallery3d.filtershow.editors.BasicEditor;
70 import com.android.gallery3d.filtershow.editors.Editor;
71 import com.android.gallery3d.filtershow.editors.EditorChanSat;
72 import com.android.gallery3d.filtershow.editors.EditorCrop;
73 import com.android.gallery3d.filtershow.editors.EditorDraw;
74 import com.android.gallery3d.filtershow.editors.EditorGrad;
75 import com.android.gallery3d.filtershow.editors.EditorManager;
76 import com.android.gallery3d.filtershow.editors.EditorMirror;
77 import com.android.gallery3d.filtershow.editors.EditorPanel;
78 import com.android.gallery3d.filtershow.editors.EditorRedEye;
79 import com.android.gallery3d.filtershow.editors.EditorRotate;
80 import com.android.gallery3d.filtershow.editors.EditorStraighten;
81 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
82 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
83 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
84 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
85 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
86 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
87 import com.android.gallery3d.filtershow.filters.FiltersManager;
88 import com.android.gallery3d.filtershow.filters.ImageFilter;
89 import com.android.gallery3d.filtershow.history.HistoryItem;
90 import com.android.gallery3d.filtershow.history.HistoryManager;
91 import com.android.gallery3d.filtershow.imageshow.ImageShow;
92 import com.android.gallery3d.filtershow.imageshow.MasterImage;
93 import com.android.gallery3d.filtershow.imageshow.Spline;
94 import com.android.gallery3d.filtershow.info.InfoPanel;
95 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
96 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
97 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
98 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
99 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
100 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
101 import com.android.gallery3d.filtershow.state.StateAdapter;
102 import com.android.gallery3d.filtershow.tools.SaveImage;
103 import com.android.gallery3d.filtershow.tools.XmpPresets;
104 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
105 import com.android.gallery3d.filtershow.ui.ExportDialog;
106 import com.android.gallery3d.filtershow.ui.FramedTextButton;
107 import com.android.gallery3d.util.GalleryUtils;
108 import com.android.gallery3d.util.UsageStatistics;
109 import com.android.photos.data.GalleryBitmapPool;
112 import java.lang.ref.WeakReference;
113 import java.util.ArrayList;
114 import java.util.Vector;
116 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
117 OnShareTargetSelectedListener {
119 private String mAction = "";
120 MasterImage mMasterImage = null;
122 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
124 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
125 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
126 private ImageShow mImageShow = null;
128 private View mSaveButton = null;
130 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
132 private static final int SELECT_PICTURE = 1;
133 private static final String LOGTAG = "FilterShowActivity";
135 private boolean mShowingTinyPlanet = false;
136 private boolean mShowingImageStatePanel = false;
137 private boolean mShowingVersionsPanel = false;
138 private boolean mShowingInformationPanel = false;
140 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
142 private ShareActionProvider mShareActionProvider;
143 private File mSharedOutputFile = null;
145 private boolean mSharingImage = false;
147 private WeakReference<ProgressDialog> mSavingProgressDialog;
149 private LoadBitmapTask mLoadBitmapTask;
151 private Uri mOriginalImageUri = null;
152 private ImagePreset mOriginalPreset = null;
154 private Uri mSelectedImageUri = null;
156 private UserPresetsManager mUserPresetsManager = null;
157 private UserPresetsAdapter mUserPresetsAdapter = null;
158 private CategoryAdapter mCategoryLooksAdapter = null;
159 private CategoryAdapter mCategoryBordersAdapter = null;
160 private CategoryAdapter mCategoryGeometryAdapter = null;
161 private CategoryAdapter mCategoryFiltersAdapter = null;
162 private CategoryAdapter mCategoryVersionsAdapter = null;
163 private int mCurrentPanel = MainPanel.LOOKS;
164 private Vector<FilterUserPresetRepresentation> mVersions =
165 new Vector<FilterUserPresetRepresentation>();
166 private int mVersionsCounter = 0;
168 private boolean mHandlingSwipeButton = false;
169 private View mHandledSwipeView = null;
170 private float mHandledSwipeViewLastDelta = 0;
171 private float mSwipeStartY = 0;
173 private ProcessingService mBoundService;
174 private boolean mIsBound = false;
176 public ProcessingService getProcessingService() {
177 return mBoundService;
180 public boolean isSimpleEditAction() {
181 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
184 private ServiceConnection mConnection = new ServiceConnection() {
186 public void onServiceConnected(ComponentName className, IBinder service) {
188 * This is called when the connection with the service has been
189 * established, giving us the service object we can use to
190 * interact with the service. Because we have bound to a explicit
191 * service that we know is running in our own process, we can
192 * cast its IBinder to a concrete class and directly access it.
194 mBoundService = ((ProcessingService.LocalBinder)service).getService();
195 mBoundService.setFiltershowActivity(FilterShowActivity.this);
196 mBoundService.onStart();
200 public void onServiceDisconnected(ComponentName className) {
202 * This is called when the connection with the service has been
203 * unexpectedly disconnected -- that is, its process crashed.
204 * Because it is running in our same process, we should never
207 mBoundService = null;
211 void doBindService() {
213 * Establish a connection with the service. We use an explicit
214 * class name because we want a specific service implementation that
215 * we know will be running in our own process (and thus won't be
216 * supporting component replacement by other applications).
218 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
219 mConnection, Context.BIND_AUTO_CREATE);
223 void doUnbindService() {
225 // Detach our existing connection.
226 unbindService(mConnection);
231 private void setupPipeline() {
233 ImageFilter.setActivityForMemoryToasts(this);
234 mUserPresetsManager = new UserPresetsManager(this);
235 mUserPresetsAdapter = new UserPresetsAdapter(this);
238 public void updateUIAfterServiceStarted() {
247 public void onCreate(Bundle savedInstanceState) {
248 super.onCreate(savedInstanceState);
250 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
251 if (onlyUsePortrait) {
252 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
254 MasterImage.setMaster(mMasterImage);
256 clearGalleryBitmapPool();
264 UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
265 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
266 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
269 public boolean isShowingImageStatePanel() {
270 return mShowingImageStatePanel;
273 public void loadMainPanel() {
274 if (findViewById(R.id.main_panel_container) == null) {
277 MainPanel panel = new MainPanel();
278 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
279 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
280 transaction.commit();
283 public void loadEditorPanel(FilterRepresentation representation,
284 final Editor currentEditor) {
285 if (representation.getEditorId() == ImageOnlyEditor.ID) {
286 currentEditor.reflectCurrentFilter();
289 final int currentId = currentEditor.getID();
290 Runnable showEditor = new Runnable() {
293 EditorPanel panel = new EditorPanel();
294 panel.setEditor(currentId);
295 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
296 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
297 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
298 transaction.commit();
301 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
302 boolean doAnimation = false;
303 if (mShowingImageStatePanel
304 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
307 if (doAnimation && main != null && main instanceof MainPanel) {
308 MainPanel mainPanel = (MainPanel) main;
309 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
310 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
311 int panelHeight = container.getHeight() + bottom.getHeight();
312 ViewPropertyAnimator anim = mainPanel.getView().animate();
313 anim.translationY(panelHeight).start();
314 final Handler handler = new Handler();
315 handler.postDelayed(showEditor, anim.getDuration());
321 private void hideInformationPanel() {
322 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
323 infoLayout.setVisibility(View.GONE);
324 Fragment fragment = getSupportFragmentManager().findFragmentByTag(InfoPanel.FRAGMENT_TAG);
325 if (fragment != null) {
326 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
327 transaction.remove(fragment);
328 transaction.commit();
330 mShowingInformationPanel = false;
333 public void showInformationPanel() {
334 mShowingInformationPanel = !mShowingInformationPanel;
335 if (!mShowingInformationPanel) {
336 hideInformationPanel();
337 showDefaultImageView();
340 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
341 transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
342 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
343 infoLayout.setVisibility(View.VISIBLE);
344 mEditorPlaceHolder.hide();
345 mImageShow.setVisibility(View.GONE);
347 InfoPanel panel = new InfoPanel();
348 transaction.replace(R.id.central_panel_container, panel, InfoPanel.FRAGMENT_TAG);
349 transaction.commit();
352 private void loadXML() {
353 setContentView(R.layout.filtershow_activity);
355 ActionBar actionBar = getActionBar();
356 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
357 actionBar.setCustomView(R.layout.filtershow_actionbar);
359 mSaveButton = actionBar.getCustomView();
360 mSaveButton.setOnClickListener(new OnClickListener() {
362 public void onClick(View view) {
367 mImageShow = (ImageShow) findViewById(R.id.imageShow);
368 mImageViews.add(mImageShow);
372 mEditorPlaceHolder.hide();
373 mImageShow.bindAsImageLoadListener();
378 public void fillCategories() {
387 public void setupStatePanel() {
388 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
391 private void fillVersions() {
392 mCategoryVersionsAdapter = new CategoryAdapter(this);
393 mCategoryVersionsAdapter.setShowAddButton(true);
396 public void updateVersions() {
397 mCategoryVersionsAdapter.clear();
398 FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
399 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
400 mCategoryVersionsAdapter.add(
401 new Action(this, originalRep, Action.FULL_VIEW));
402 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
403 FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
404 getString(R.string.filtershow_version_current), current, -1);
405 mCategoryVersionsAdapter.add(
406 new Action(this, currentRep, Action.FULL_VIEW));
407 if (mVersions.size() > 0) {
408 mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
410 for (FilterUserPresetRepresentation rep : mVersions) {
411 mCategoryVersionsAdapter.add(
412 new Action(this, rep, Action.FULL_VIEW, true));
414 mCategoryVersionsAdapter.notifyDataSetInvalidated();
417 public void addCurrentVersion() {
418 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
420 FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
421 "" + mVersionsCounter, current, -1);
426 public void removeVersion(Action action) {
427 mVersions.remove(action.getRepresentation());
431 public void removeLook(Action action) {
432 FilterUserPresetRepresentation rep =
433 (FilterUserPresetRepresentation) action.getRepresentation();
437 mUserPresetsManager.delete(rep.getId());
438 updateUserPresetsFromManager();
441 private void fillEffects() {
442 FiltersManager filtersManager = FiltersManager.getManager();
443 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
444 mCategoryFiltersAdapter = new CategoryAdapter(this);
445 for (FilterRepresentation representation : filtersRepresentations) {
446 if (representation.getTextId() != 0) {
447 representation.setName(getString(representation.getTextId()));
449 mCategoryFiltersAdapter.add(new Action(this, representation));
453 private void fillTools() {
454 FiltersManager filtersManager = FiltersManager.getManager();
455 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
456 mCategoryGeometryAdapter = new CategoryAdapter(this);
457 for (FilterRepresentation representation : filtersRepresentations) {
458 mCategoryGeometryAdapter.add(new Action(this, representation));
462 private void processIntent() {
463 Intent intent = getIntent();
464 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
465 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
468 mAction = intent.getAction();
469 mSelectedImageUri = intent.getData();
470 Uri loadUri = mSelectedImageUri;
471 if (mOriginalImageUri != null) {
472 loadUri = mOriginalImageUri;
474 if (loadUri != null) {
475 startLoadBitmap(loadUri);
481 private void setupEditors() {
482 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
483 EditorManager.addEditors(mEditorPlaceHolder);
484 mEditorPlaceHolder.setOldViews(mImageViews);
487 private void fillEditors() {
488 mEditorPlaceHolder.addEditor(new EditorChanSat());
489 mEditorPlaceHolder.addEditor(new EditorGrad());
490 mEditorPlaceHolder.addEditor(new EditorDraw());
491 mEditorPlaceHolder.addEditor(new BasicEditor());
492 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
493 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
494 mEditorPlaceHolder.addEditor(new EditorRedEye());
495 mEditorPlaceHolder.addEditor(new EditorCrop());
496 mEditorPlaceHolder.addEditor(new EditorMirror());
497 mEditorPlaceHolder.addEditor(new EditorRotate());
498 mEditorPlaceHolder.addEditor(new EditorStraighten());
501 private void setDefaultValues() {
502 Resources res = getResources();
504 // TODO: get those values from XML.
505 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
506 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
507 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
509 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
510 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
511 Spline.setCurveHandle(curveHandle, curveHandleSize);
512 Spline.setCurveWidth((int) getPixelsFromDip(3));
515 private void startLoadBitmap(Uri uri) {
516 final View loading = findViewById(R.id.loading);
517 final View imageShow = findViewById(R.id.imageShow);
518 imageShow.setVisibility(View.INVISIBLE);
519 loading.setVisibility(View.VISIBLE);
520 mShowingTinyPlanet = false;
521 mLoadBitmapTask = new LoadBitmapTask();
522 mLoadBitmapTask.execute(uri);
525 private void fillBorders() {
526 FiltersManager filtersManager = FiltersManager.getManager();
527 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
529 for (int i = 0; i < borders.size(); i++) {
530 FilterRepresentation filter = borders.get(i);
531 filter.setName(getString(R.string.borders));
533 filter.setName(getString(R.string.none));
537 mCategoryBordersAdapter = new CategoryAdapter(this);
538 for (FilterRepresentation representation : borders) {
539 if (representation.getTextId() != 0) {
540 representation.setName(getString(representation.getTextId()));
542 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
546 public UserPresetsAdapter getUserPresetsAdapter() {
547 return mUserPresetsAdapter;
550 public CategoryAdapter getCategoryLooksAdapter() {
551 return mCategoryLooksAdapter;
554 public CategoryAdapter getCategoryBordersAdapter() {
555 return mCategoryBordersAdapter;
558 public CategoryAdapter getCategoryGeometryAdapter() {
559 return mCategoryGeometryAdapter;
562 public CategoryAdapter getCategoryFiltersAdapter() {
563 return mCategoryFiltersAdapter;
566 public CategoryAdapter getCategoryVersionsAdapter() {
567 return mCategoryVersionsAdapter;
570 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
571 if (filterRepresentation == null) {
574 ImagePreset oldPreset = MasterImage.getImage().getPreset();
575 ImagePreset copy = new ImagePreset(oldPreset);
576 copy.removeFilter(filterRepresentation);
577 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
578 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
579 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
580 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
584 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
585 if (filterRepresentation == null) {
588 if (!(filterRepresentation instanceof FilterRotateRepresentation)
589 && !(filterRepresentation instanceof FilterMirrorRepresentation)
590 && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
593 ImagePreset oldPreset = MasterImage.getImage().getPreset();
594 ImagePreset copy = new ImagePreset(oldPreset);
595 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
596 if (representation == null) {
597 copy.addFilter(filterRepresentation);
598 } else if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
599 representation.useParametersFrom(filterRepresentation);
600 filterRepresentation = representation;
602 if (filterRepresentation.allowsSingleInstanceOnly()) {
603 // Don't just update the filter representation. Centralize the
604 // logic in the addFilter(), such that we can keep "None" as
606 copy.removeFilter(representation);
607 copy.addFilter(filterRepresentation);
610 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
611 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
614 public void showRepresentation(FilterRepresentation representation) {
615 if (representation == null) {
619 if (representation instanceof FilterRotateRepresentation) {
620 FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
623 if (representation instanceof FilterMirrorRepresentation) {
624 FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
627 useFilterRepresentation(representation);
629 // show representation
630 Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
631 loadEditorPanel(representation, mCurrentEditor);
632 hideInformationPanel();
635 public Editor getEditor(int editorID) {
636 return mEditorPlaceHolder.getEditor(editorID);
639 public void setCurrentPanel(int currentPanel) {
640 mCurrentPanel = currentPanel;
643 public int getCurrentPanel() {
644 return mCurrentPanel;
647 public void updateCategories() {
648 ImagePreset preset = mMasterImage.getPreset();
649 mCategoryLooksAdapter.reflectImagePreset(preset);
650 mCategoryBordersAdapter.reflectImagePreset(preset);
653 public View getMainStatePanelContainer(int id) {
654 return findViewById(id);
657 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
659 protected Boolean doInBackground(Void... params) {
660 MasterImage master = MasterImage.getImage();
661 Rect originalBounds = master.getOriginalBounds();
662 if (master.supportsHighRes()) {
663 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
664 if (highresPreviewSize > originalBounds.width()) {
665 highresPreviewSize = originalBounds.width();
667 Rect bounds = new Rect();
668 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
669 master.getActivity(), highresPreviewSize,
670 master.getOrientation(), bounds);
671 master.setOriginalBounds(bounds);
672 master.setOriginalBitmapHighres(originalHires);
673 mBoundService.setOriginalBitmapHighres(originalHires);
674 master.warnListeners();
680 protected void onPostExecute(Boolean result) {
681 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
682 if (highresBitmap != null) {
683 float highResPreviewScale = (float) highresBitmap.getWidth()
684 / (float) MasterImage.getImage().getOriginalBounds().width();
685 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
690 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
693 public LoadBitmapTask() {
694 mBitmapSize = getScreenImageSize();
698 protected Boolean doInBackground(Uri... params) {
699 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
702 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
707 protected void onProgressUpdate(Boolean... values) {
708 super.onProgressUpdate(values);
713 mShowingTinyPlanet = true;
718 protected void onPostExecute(Boolean result) {
719 MasterImage.setMaster(mMasterImage);
726 // TODO: We should figure out the best way preventing this from
727 // happening, e.g: early checking.
731 if (null == CachingPipeline.getRenderScriptContext()){
732 Log.v(LOGTAG,"RenderScript context destroyed during load");
735 final View loading = findViewById(R.id.loading);
736 loading.setVisibility(View.GONE);
737 final View imageShow = findViewById(R.id.imageShow);
738 imageShow.setVisibility(View.VISIBLE);
740 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
741 mBoundService.setOriginalBitmap(largeBitmap);
742 MasterImage.getImage().resetGeometryImages();
744 float previewScale = (float) largeBitmap.getWidth()
745 / (float) MasterImage.getImage().getOriginalBounds().width();
746 mBoundService.setPreviewScaleFactor(previewScale);
747 if (!mShowingTinyPlanet) {
748 mCategoryFiltersAdapter.removeTinyPlanet();
750 mCategoryLooksAdapter.imageLoaded();
751 mCategoryBordersAdapter.imageLoaded();
752 mCategoryGeometryAdapter.imageLoaded();
753 mCategoryFiltersAdapter.imageLoaded();
754 mLoadBitmapTask = null;
756 if (mOriginalPreset != null) {
757 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
758 MasterImage.getImage().setPreset(mOriginalPreset,
759 mOriginalPreset.getLastRepresentation(), true);
760 mOriginalPreset = null;
763 if (mAction == TINY_PLANET_ACTION) {
764 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
766 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
767 highresLoad.execute();
768 super.onPostExecute(result);
773 private void clearGalleryBitmapPool() {
774 (new AsyncTask<Void, Void, Void>() {
776 protected Void doInBackground(Void... params) {
777 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
778 GalleryBitmapPool.getInstance().clear();
785 protected void onDestroy() {
786 if (mLoadBitmapTask != null) {
787 mLoadBitmapTask.cancel(false);
789 mUserPresetsManager.close();
794 // TODO: find a more robust way of handling image size selection
795 // for high screen densities.
796 private int getScreenImageSize() {
797 DisplayMetrics outMetrics = new DisplayMetrics();
798 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
799 return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
802 private void showSavingProgress(String albumName) {
803 ProgressDialog progress;
804 if (mSavingProgressDialog != null) {
805 progress = mSavingProgressDialog.get();
806 if (progress != null) {
811 // TODO: Allow cancellation of the saving process
813 if (albumName == null) {
814 progressText = getString(R.string.saving_image);
816 progressText = getString(R.string.filtershow_saving_image, albumName);
818 progress = ProgressDialog.show(this, "", progressText, true, false);
819 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
822 private void hideSavingProgress() {
823 if (mSavingProgressDialog != null) {
824 ProgressDialog progress = mSavingProgressDialog.get();
825 if (progress != null)
830 public void completeSaveImage(Uri saveUri) {
831 if (mSharingImage && mSharedOutputFile != null) {
832 // Image saved, we unblock the content provider
833 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
834 Uri.encode(mSharedOutputFile.getAbsolutePath()));
835 ContentValues values = new ContentValues();
836 values.put(SharedImageProvider.PREPARE, false);
837 getContentResolver().insert(uri, values);
839 setResult(RESULT_OK, new Intent().setData(saveUri));
840 hideSavingProgress();
845 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
846 // First, let's tell the SharedImageProvider that it will need to wait
848 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
849 Uri.encode(mSharedOutputFile.getAbsolutePath()));
850 ContentValues values = new ContentValues();
851 values.put(SharedImageProvider.PREPARE, true);
852 getContentResolver().insert(uri, values);
853 mSharingImage = true;
855 // Process and save the image in the background.
856 showSavingProgress(null);
857 mImageShow.saveImage(this, mSharedOutputFile);
861 private Intent getDefaultShareIntent() {
862 Intent intent = new Intent(Intent.ACTION_SEND);
863 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
864 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
865 intent.setType(SharedImageProvider.MIME_TYPE);
866 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
867 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
868 Uri.encode(mSharedOutputFile.getAbsolutePath()));
869 intent.putExtra(Intent.EXTRA_STREAM, uri);
874 public boolean onCreateOptionsMenu(Menu menu) {
875 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
876 MenuItem showState = menu.findItem(R.id.showImageStateButton);
877 if (mShowingImageStatePanel) {
878 showState.setTitle(R.string.hide_imagestate_panel);
880 showState.setTitle(R.string.show_imagestate_panel);
882 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
883 .getActionProvider();
884 mShareActionProvider.setShareIntent(getDefaultShareIntent());
885 mShareActionProvider.setOnShareTargetSelectedListener(this);
887 MenuItem undoItem = menu.findItem(R.id.undoButton);
888 MenuItem redoItem = menu.findItem(R.id.redoButton);
889 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
890 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
895 public void onPause() {
897 if (mShareActionProvider != null) {
898 mShareActionProvider.setOnShareTargetSelectedListener(null);
903 public void onResume() {
905 if (mShareActionProvider != null) {
906 mShareActionProvider.setOnShareTargetSelectedListener(this);
911 public boolean onOptionsItemSelected(MenuItem item) {
912 switch (item.getItemId()) {
913 case R.id.undoButton: {
914 HistoryManager adapter = mMasterImage.getHistory();
915 int position = adapter.undo();
916 mMasterImage.onHistoryItemClick(position);
919 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
920 UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
923 case R.id.redoButton: {
924 HistoryManager adapter = mMasterImage.getHistory();
925 int position = adapter.redo();
926 mMasterImage.onHistoryItemClick(position);
928 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
929 UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
932 case R.id.resetHistoryButton: {
934 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
935 UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
938 case R.id.showImageStateButton: {
939 toggleImageStatePanel();
940 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
941 UsageStatistics.CATEGORY_BUTTON_PRESS,
942 mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
945 case R.id.exportFlattenButton: {
946 showExportOptionsDialog();
949 case android.R.id.home: {
953 case R.id.manageUserPresets: {
957 case R.id.showInfoPanel: {
958 showInformationPanel();
965 public void addNewPreset() {
966 DialogFragment dialog = new PresetManagementDialog();
967 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
970 private void manageUserPresets() {
971 DialogFragment dialog = new PresetManagementDialog();
972 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
975 private void showExportOptionsDialog() {
976 DialogFragment dialog = new ExportDialog();
977 dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
980 public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
981 ArrayList<FilterUserPresetRepresentation> representations =
982 adapter.getDeletedRepresentations();
983 for (FilterUserPresetRepresentation representation : representations) {
984 deletePreset(representation.getId());
986 ArrayList<FilterUserPresetRepresentation> changedRepresentations =
987 adapter.getChangedRepresentations();
988 for (FilterUserPresetRepresentation representation : changedRepresentations) {
989 updatePreset(representation);
991 adapter.clearDeletedRepresentations();
992 adapter.clearChangedRepresentations();
996 public void loadUserPresets() {
997 mUserPresetsManager.load();
998 updateUserPresetsFromManager();
1001 public void updateUserPresetsFromManager() {
1002 ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1003 if (presets == null) {
1006 if (mCategoryLooksAdapter != null) {
1009 if (presets.size() > 0) {
1010 mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1012 mUserPresetsAdapter.clear();
1013 for (int i = 0; i < presets.size(); i++) {
1014 FilterUserPresetRepresentation representation = presets.get(i);
1015 mCategoryLooksAdapter.add(
1016 new Action(this, representation, Action.FULL_VIEW, true));
1017 mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1019 if (presets.size() > 0) {
1020 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1022 mCategoryLooksAdapter.notifyDataSetChanged();
1023 mCategoryLooksAdapter.notifyDataSetInvalidated();
1026 public void saveCurrentImagePreset(String name) {
1027 mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1030 private void deletePreset(int id) {
1031 mUserPresetsManager.delete(id);
1034 private void updatePreset(FilterUserPresetRepresentation representation) {
1035 mUserPresetsManager.update(representation);
1038 public void enableSave(boolean enable) {
1039 if (mSaveButton != null) {
1040 mSaveButton.setEnabled(enable);
1044 private void fillLooks() {
1045 FiltersManager filtersManager = FiltersManager.getManager();
1046 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1048 mCategoryLooksAdapter = new CategoryAdapter(this);
1049 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1050 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1051 for (FilterRepresentation representation : filtersRepresentations) {
1052 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1054 if (mUserPresetsManager.getRepresentations() == null
1055 || mUserPresetsManager.getRepresentations().size() == 0) {
1056 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1059 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1060 if (panel != null) {
1061 if (panel instanceof MainPanel) {
1062 MainPanel mainPanel = (MainPanel) panel;
1063 mainPanel.loadCategoryLookPanel(true);
1068 public void setDefaultPreset() {
1069 // Default preset (original)
1070 ImagePreset preset = new ImagePreset(); // empty
1071 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1074 // //////////////////////////////////////////////////////////////////////////////
1075 // Some utility functions
1076 // TODO: finish the cleanup.
1078 public void invalidateViews() {
1079 for (ImageShow views : mImageViews) {
1080 views.updateImage();
1084 public void hideImageViews() {
1085 for (View view : mImageViews) {
1086 view.setVisibility(View.GONE);
1088 mEditorPlaceHolder.hide();
1091 // //////////////////////////////////////////////////////////////////////////////
1092 // imageState panel...
1094 public void toggleImageStatePanel() {
1095 invalidateOptionsMenu();
1096 mShowingImageStatePanel = !mShowingImageStatePanel;
1097 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1098 if (panel != null) {
1099 if (panel instanceof EditorPanel) {
1100 EditorPanel editorPanel = (EditorPanel) panel;
1101 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1102 } else if (panel instanceof MainPanel) {
1103 MainPanel mainPanel = (MainPanel) panel;
1104 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1109 public void toggleVersionsPanel() {
1110 mShowingVersionsPanel = !mShowingVersionsPanel;
1111 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1112 if (panel != null && panel instanceof MainPanel) {
1113 MainPanel mainPanel = (MainPanel) panel;
1114 mainPanel.loadCategoryVersionsPanel();
1119 public void onConfigurationChanged(Configuration newConfig)
1121 super.onConfigurationChanged(newConfig);
1127 // mLoadBitmapTask==null implies you have looked at the intent
1128 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1129 mCategoryFiltersAdapter.removeTinyPlanet();
1131 final View loading = findViewById(R.id.loading);
1132 loading.setVisibility(View.GONE);
1135 public void setupMasterImage() {
1137 HistoryManager historyManager = new HistoryManager();
1138 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1139 MasterImage.reset();
1140 mMasterImage = MasterImage.getImage();
1141 mMasterImage.setHistoryManager(historyManager);
1142 mMasterImage.setStateAdapter(imageStateAdapter);
1143 mMasterImage.setActivity(this);
1145 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1146 mMasterImage.setSupportsHighRes(true);
1148 mMasterImage.setSupportsHighRes(false);
1152 void resetHistory() {
1153 HistoryManager adapter = mMasterImage.getHistory();
1155 HistoryItem historyItem = adapter.getItem(0);
1156 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
1157 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
1162 public void showDefaultImageView() {
1163 hideInformationPanel();
1164 mEditorPlaceHolder.hide();
1165 mImageShow.setVisibility(View.VISIBLE);
1166 MasterImage.getImage().setCurrentFilter(null);
1167 MasterImage.getImage().setCurrentFilterRepresentation(null);
1170 public void backToMain() {
1171 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1172 if (currentPanel instanceof MainPanel) {
1176 showDefaultImageView();
1180 public void onBackPressed() {
1181 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1182 if (currentPanel instanceof MainPanel) {
1183 if (!mImageShow.hasModifications()) {
1186 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1187 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1188 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1190 public void onClick(DialogInterface dialog, int id) {
1194 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1196 public void onClick(DialogInterface dialog, int id) {
1207 public void cannotLoadImage() {
1208 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1212 // //////////////////////////////////////////////////////////////////////////////
1214 public float getPixelsFromDip(float value) {
1215 Resources r = getResources();
1216 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1217 r.getDisplayMetrics());
1221 public void onItemClick(AdapterView<?> parent, View view, int position,
1223 mMasterImage.onHistoryItemClick(position);
1227 public void pickImage() {
1228 Intent intent = new Intent();
1229 intent.setType("image/*");
1230 intent.setAction(Intent.ACTION_GET_CONTENT);
1231 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1236 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1237 if (resultCode == RESULT_OK) {
1238 if (requestCode == SELECT_PICTURE) {
1239 Uri selectedImageUri = data.getData();
1240 startLoadBitmap(selectedImageUri);
1246 public void saveImage() {
1247 if (mImageShow.hasModifications()) {
1248 // Get the name of the album, to which the image will be saved
1249 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1250 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1251 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1252 showSavingProgress(albumName);
1253 mImageShow.saveImage(this, null);
1260 public void done() {
1261 hideSavingProgress();
1262 if (mLoadBitmapTask != null) {
1263 mLoadBitmapTask.cancel(false);
1268 private void extractXMPData() {
1269 XMresults res = XmpPresets.extractXMPData(
1270 getBaseContext(), mMasterImage, getIntent().getData());
1274 mOriginalImageUri = res.originalimage;
1275 mOriginalPreset = res.preset;
1278 public Uri getSelectedImageUri() {
1279 return mSelectedImageUri;
1282 public void setHandlesSwipeForView(View view, float startY) {
1284 mHandlingSwipeButton = true;
1286 mHandlingSwipeButton = false;
1288 mHandledSwipeView = view;
1289 int[] location = new int[2];
1290 view.getLocationInWindow(location);
1291 mSwipeStartY = location[1] + startY;
1294 public boolean dispatchTouchEvent (MotionEvent ev) {
1295 if (mHandlingSwipeButton) {
1296 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1297 float delta = ev.getY() - mSwipeStartY;
1298 mHandledSwipeView.setTranslationY(delta);
1299 delta = Math.abs(delta);
1300 float transparency = Math.min(1, delta / mHandledSwipeView.getHeight());
1301 mHandledSwipeView.setAlpha(1.f - transparency);
1302 mHandledSwipeViewLastDelta = delta;
1304 if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1305 || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1306 mHandledSwipeView.setTranslationY(0);
1307 mHandledSwipeView.setAlpha(1.f);
1308 mHandlingSwipeButton = false;
1309 if (mHandledSwipeViewLastDelta > mHandledSwipeView.getHeight()) {
1310 ((SwipableView) mHandledSwipeView).delete();
1315 return super.dispatchTouchEvent(ev);