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.CategoryView;
67 import com.android.gallery3d.filtershow.category.MainPanel;
68 import com.android.gallery3d.filtershow.category.SwipableView;
69 import com.android.gallery3d.filtershow.data.UserPresetsManager;
70 import com.android.gallery3d.filtershow.editors.BasicEditor;
71 import com.android.gallery3d.filtershow.editors.Editor;
72 import com.android.gallery3d.filtershow.editors.EditorChanSat;
73 import com.android.gallery3d.filtershow.editors.EditorCrop;
74 import com.android.gallery3d.filtershow.editors.EditorDraw;
75 import com.android.gallery3d.filtershow.editors.EditorGrad;
76 import com.android.gallery3d.filtershow.editors.EditorManager;
77 import com.android.gallery3d.filtershow.editors.EditorMirror;
78 import com.android.gallery3d.filtershow.editors.EditorPanel;
79 import com.android.gallery3d.filtershow.editors.EditorRedEye;
80 import com.android.gallery3d.filtershow.editors.EditorRotate;
81 import com.android.gallery3d.filtershow.editors.EditorStraighten;
82 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
83 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
84 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
85 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
86 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
87 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
88 import com.android.gallery3d.filtershow.filters.FiltersManager;
89 import com.android.gallery3d.filtershow.filters.ImageFilter;
90 import com.android.gallery3d.filtershow.history.HistoryItem;
91 import com.android.gallery3d.filtershow.history.HistoryManager;
92 import com.android.gallery3d.filtershow.imageshow.ImageShow;
93 import com.android.gallery3d.filtershow.imageshow.MasterImage;
94 import com.android.gallery3d.filtershow.imageshow.Spline;
95 import com.android.gallery3d.filtershow.info.InfoPanel;
96 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
97 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
98 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
99 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
100 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
101 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
102 import com.android.gallery3d.filtershow.state.StateAdapter;
103 import com.android.gallery3d.filtershow.tools.SaveImage;
104 import com.android.gallery3d.filtershow.tools.XmpPresets;
105 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
106 import com.android.gallery3d.filtershow.ui.ExportDialog;
107 import com.android.gallery3d.filtershow.ui.FramedTextButton;
108 import com.android.gallery3d.util.GalleryUtils;
109 import com.android.gallery3d.util.UsageStatistics;
110 import com.android.photos.data.GalleryBitmapPool;
113 import java.lang.ref.WeakReference;
114 import java.util.ArrayList;
115 import java.util.Vector;
117 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
118 OnShareTargetSelectedListener {
120 private String mAction = "";
121 MasterImage mMasterImage = null;
123 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
125 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
126 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
127 private ImageShow mImageShow = null;
129 private View mSaveButton = null;
131 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
133 private static final int SELECT_PICTURE = 1;
134 private static final String LOGTAG = "FilterShowActivity";
136 private boolean mShowingTinyPlanet = false;
137 private boolean mShowingImageStatePanel = false;
138 private boolean mShowingVersionsPanel = false;
139 private boolean mShowingInformationPanel = false;
141 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
143 private ShareActionProvider mShareActionProvider;
144 private File mSharedOutputFile = null;
146 private boolean mSharingImage = false;
148 private WeakReference<ProgressDialog> mSavingProgressDialog;
150 private LoadBitmapTask mLoadBitmapTask;
152 private Uri mOriginalImageUri = null;
153 private ImagePreset mOriginalPreset = null;
155 private Uri mSelectedImageUri = null;
157 private UserPresetsManager mUserPresetsManager = null;
158 private UserPresetsAdapter mUserPresetsAdapter = null;
159 private CategoryAdapter mCategoryLooksAdapter = null;
160 private CategoryAdapter mCategoryBordersAdapter = null;
161 private CategoryAdapter mCategoryGeometryAdapter = null;
162 private CategoryAdapter mCategoryFiltersAdapter = null;
163 private CategoryAdapter mCategoryVersionsAdapter = null;
164 private int mCurrentPanel = MainPanel.LOOKS;
165 private Vector<FilterUserPresetRepresentation> mVersions =
166 new Vector<FilterUserPresetRepresentation>();
167 private int mVersionsCounter = 0;
169 private boolean mHandlingSwipeButton = false;
170 private View mHandledSwipeView = null;
171 private float mHandledSwipeViewLastDelta = 0;
172 private float mSwipeStartX = 0;
173 private float mSwipeStartY = 0;
175 private ProcessingService mBoundService;
176 private boolean mIsBound = false;
178 public ProcessingService getProcessingService() {
179 return mBoundService;
182 public boolean isSimpleEditAction() {
183 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
186 private ServiceConnection mConnection = new ServiceConnection() {
188 public void onServiceConnected(ComponentName className, IBinder service) {
190 * This is called when the connection with the service has been
191 * established, giving us the service object we can use to
192 * interact with the service. Because we have bound to a explicit
193 * service that we know is running in our own process, we can
194 * cast its IBinder to a concrete class and directly access it.
196 mBoundService = ((ProcessingService.LocalBinder)service).getService();
197 mBoundService.setFiltershowActivity(FilterShowActivity.this);
198 mBoundService.onStart();
202 public void onServiceDisconnected(ComponentName className) {
204 * This is called when the connection with the service has been
205 * unexpectedly disconnected -- that is, its process crashed.
206 * Because it is running in our same process, we should never
209 mBoundService = null;
213 void doBindService() {
215 * Establish a connection with the service. We use an explicit
216 * class name because we want a specific service implementation that
217 * we know will be running in our own process (and thus won't be
218 * supporting component replacement by other applications).
220 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
221 mConnection, Context.BIND_AUTO_CREATE);
225 void doUnbindService() {
227 // Detach our existing connection.
228 unbindService(mConnection);
233 private void setupPipeline() {
235 ImageFilter.setActivityForMemoryToasts(this);
236 mUserPresetsManager = new UserPresetsManager(this);
237 mUserPresetsAdapter = new UserPresetsAdapter(this);
240 public void updateUIAfterServiceStarted() {
249 public void onCreate(Bundle savedInstanceState) {
250 super.onCreate(savedInstanceState);
252 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
253 if (onlyUsePortrait) {
254 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
256 MasterImage.setMaster(mMasterImage);
258 clearGalleryBitmapPool();
266 UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
267 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
268 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
271 public boolean isShowingImageStatePanel() {
272 return mShowingImageStatePanel;
275 public void loadMainPanel() {
276 if (findViewById(R.id.main_panel_container) == null) {
279 MainPanel panel = new MainPanel();
280 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
281 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
282 transaction.commit();
285 public void loadEditorPanel(FilterRepresentation representation,
286 final Editor currentEditor) {
287 if (representation.getEditorId() == ImageOnlyEditor.ID) {
288 currentEditor.reflectCurrentFilter();
291 final int currentId = currentEditor.getID();
292 Runnable showEditor = new Runnable() {
295 EditorPanel panel = new EditorPanel();
296 panel.setEditor(currentId);
297 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
298 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
299 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
300 transaction.commit();
303 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
304 boolean doAnimation = false;
305 if (mShowingImageStatePanel
306 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
309 if (doAnimation && main != null && main instanceof MainPanel) {
310 MainPanel mainPanel = (MainPanel) main;
311 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
312 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
313 int panelHeight = container.getHeight() + bottom.getHeight();
314 ViewPropertyAnimator anim = mainPanel.getView().animate();
315 anim.translationY(panelHeight).start();
316 final Handler handler = new Handler();
317 handler.postDelayed(showEditor, anim.getDuration());
323 public void hideInformationPanel() {
324 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
325 infoLayout.setVisibility(View.GONE);
326 Fragment fragment = getSupportFragmentManager().findFragmentByTag(InfoPanel.FRAGMENT_TAG);
327 if (fragment != null) {
328 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
329 transaction.remove(fragment);
330 transaction.commit();
332 mShowingInformationPanel = false;
335 public void toggleInformationPanel() {
336 mShowingInformationPanel = !mShowingInformationPanel;
337 if (!mShowingInformationPanel) {
338 hideInformationPanel();
339 showDefaultImageView();
342 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
343 transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
344 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
345 infoLayout.setVisibility(View.VISIBLE);
346 mEditorPlaceHolder.hide();
347 mImageShow.setVisibility(View.GONE);
349 InfoPanel panel = new InfoPanel();
350 transaction.replace(R.id.central_panel_container, panel, InfoPanel.FRAGMENT_TAG);
351 transaction.commit();
354 private void loadXML() {
355 setContentView(R.layout.filtershow_activity);
357 ActionBar actionBar = getActionBar();
358 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
359 actionBar.setCustomView(R.layout.filtershow_actionbar);
361 mSaveButton = actionBar.getCustomView();
362 mSaveButton.setOnClickListener(new OnClickListener() {
364 public void onClick(View view) {
369 mImageShow = (ImageShow) findViewById(R.id.imageShow);
370 mImageViews.add(mImageShow);
374 mEditorPlaceHolder.hide();
375 mImageShow.bindAsImageLoadListener();
380 public void fillCategories() {
389 public void setupStatePanel() {
390 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
393 private void fillVersions() {
394 mCategoryVersionsAdapter = new CategoryAdapter(this);
395 mCategoryVersionsAdapter.setShowAddButton(true);
398 public void updateVersions() {
399 mCategoryVersionsAdapter.clear();
400 FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
401 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
402 mCategoryVersionsAdapter.add(
403 new Action(this, originalRep, Action.FULL_VIEW));
404 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
405 FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
406 getString(R.string.filtershow_version_current), current, -1);
407 mCategoryVersionsAdapter.add(
408 new Action(this, currentRep, Action.FULL_VIEW));
409 if (mVersions.size() > 0) {
410 mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
412 for (FilterUserPresetRepresentation rep : mVersions) {
413 mCategoryVersionsAdapter.add(
414 new Action(this, rep, Action.FULL_VIEW, true));
416 mCategoryVersionsAdapter.notifyDataSetInvalidated();
419 public void addCurrentVersion() {
420 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
422 FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
423 "" + mVersionsCounter, current, -1);
428 public void removeVersion(Action action) {
429 mVersions.remove(action.getRepresentation());
433 public void removeLook(Action action) {
434 FilterUserPresetRepresentation rep =
435 (FilterUserPresetRepresentation) action.getRepresentation();
439 mUserPresetsManager.delete(rep.getId());
440 updateUserPresetsFromManager();
443 private void fillEffects() {
444 FiltersManager filtersManager = FiltersManager.getManager();
445 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
446 mCategoryFiltersAdapter = new CategoryAdapter(this);
447 for (FilterRepresentation representation : filtersRepresentations) {
448 if (representation.getTextId() != 0) {
449 representation.setName(getString(representation.getTextId()));
451 mCategoryFiltersAdapter.add(new Action(this, representation));
455 private void fillTools() {
456 FiltersManager filtersManager = FiltersManager.getManager();
457 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
458 mCategoryGeometryAdapter = new CategoryAdapter(this);
459 for (FilterRepresentation representation : filtersRepresentations) {
460 mCategoryGeometryAdapter.add(new Action(this, representation));
464 private void processIntent() {
465 Intent intent = getIntent();
466 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
467 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
470 mAction = intent.getAction();
471 mSelectedImageUri = intent.getData();
472 Uri loadUri = mSelectedImageUri;
473 if (mOriginalImageUri != null) {
474 loadUri = mOriginalImageUri;
476 if (loadUri != null) {
477 startLoadBitmap(loadUri);
483 private void setupEditors() {
484 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
485 EditorManager.addEditors(mEditorPlaceHolder);
486 mEditorPlaceHolder.setOldViews(mImageViews);
489 private void fillEditors() {
490 mEditorPlaceHolder.addEditor(new EditorChanSat());
491 mEditorPlaceHolder.addEditor(new EditorGrad());
492 mEditorPlaceHolder.addEditor(new EditorDraw());
493 mEditorPlaceHolder.addEditor(new BasicEditor());
494 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
495 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
496 mEditorPlaceHolder.addEditor(new EditorRedEye());
497 mEditorPlaceHolder.addEditor(new EditorCrop());
498 mEditorPlaceHolder.addEditor(new EditorMirror());
499 mEditorPlaceHolder.addEditor(new EditorRotate());
500 mEditorPlaceHolder.addEditor(new EditorStraighten());
503 private void setDefaultValues() {
504 Resources res = getResources();
506 // TODO: get those values from XML.
507 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
508 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
509 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
511 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
512 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
513 Spline.setCurveHandle(curveHandle, curveHandleSize);
514 Spline.setCurveWidth((int) getPixelsFromDip(3));
517 private void startLoadBitmap(Uri uri) {
518 final View loading = findViewById(R.id.loading);
519 final View imageShow = findViewById(R.id.imageShow);
520 imageShow.setVisibility(View.INVISIBLE);
521 loading.setVisibility(View.VISIBLE);
522 mShowingTinyPlanet = false;
523 mLoadBitmapTask = new LoadBitmapTask();
524 mLoadBitmapTask.execute(uri);
527 private void fillBorders() {
528 FiltersManager filtersManager = FiltersManager.getManager();
529 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
531 for (int i = 0; i < borders.size(); i++) {
532 FilterRepresentation filter = borders.get(i);
533 filter.setName(getString(R.string.borders));
535 filter.setName(getString(R.string.none));
539 mCategoryBordersAdapter = new CategoryAdapter(this);
540 for (FilterRepresentation representation : borders) {
541 if (representation.getTextId() != 0) {
542 representation.setName(getString(representation.getTextId()));
544 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
548 public UserPresetsAdapter getUserPresetsAdapter() {
549 return mUserPresetsAdapter;
552 public CategoryAdapter getCategoryLooksAdapter() {
553 return mCategoryLooksAdapter;
556 public CategoryAdapter getCategoryBordersAdapter() {
557 return mCategoryBordersAdapter;
560 public CategoryAdapter getCategoryGeometryAdapter() {
561 return mCategoryGeometryAdapter;
564 public CategoryAdapter getCategoryFiltersAdapter() {
565 return mCategoryFiltersAdapter;
568 public CategoryAdapter getCategoryVersionsAdapter() {
569 return mCategoryVersionsAdapter;
572 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
573 if (filterRepresentation == null) {
576 ImagePreset oldPreset = MasterImage.getImage().getPreset();
577 ImagePreset copy = new ImagePreset(oldPreset);
578 copy.removeFilter(filterRepresentation);
579 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
580 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
581 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
582 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
586 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
587 if (filterRepresentation == null) {
590 if (!(filterRepresentation instanceof FilterRotateRepresentation)
591 && !(filterRepresentation instanceof FilterMirrorRepresentation)
592 && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
595 ImagePreset oldPreset = MasterImage.getImage().getPreset();
596 ImagePreset copy = new ImagePreset(oldPreset);
597 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
598 if (representation == null) {
599 filterRepresentation = filterRepresentation.copy();
600 copy.addFilter(filterRepresentation);
601 } else if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
602 representation.useParametersFrom(filterRepresentation);
603 filterRepresentation = representation;
605 if (filterRepresentation.allowsSingleInstanceOnly()) {
606 // Don't just update the filter representation. Centralize the
607 // logic in the addFilter(), such that we can keep "None" as
609 if (!representation.equals(filterRepresentation)) {
610 // Only do this if the filter isn't the same
611 // (state panel clicks can lead us here)
612 copy.removeFilter(representation);
613 copy.addFilter(filterRepresentation);
617 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
618 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
621 public void showRepresentation(FilterRepresentation representation) {
622 if (representation == null) {
626 if (representation instanceof FilterRotateRepresentation) {
627 FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
630 if (representation instanceof FilterMirrorRepresentation) {
631 FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
634 useFilterRepresentation(representation);
636 // show representation
637 Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
638 loadEditorPanel(representation, mCurrentEditor);
639 hideInformationPanel();
642 public Editor getEditor(int editorID) {
643 return mEditorPlaceHolder.getEditor(editorID);
646 public void setCurrentPanel(int currentPanel) {
647 mCurrentPanel = currentPanel;
650 public int getCurrentPanel() {
651 return mCurrentPanel;
654 public void updateCategories() {
655 ImagePreset preset = mMasterImage.getPreset();
656 mCategoryLooksAdapter.reflectImagePreset(preset);
657 mCategoryBordersAdapter.reflectImagePreset(preset);
660 public View getMainStatePanelContainer(int id) {
661 return findViewById(id);
664 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
666 protected Boolean doInBackground(Void... params) {
667 MasterImage master = MasterImage.getImage();
668 Rect originalBounds = master.getOriginalBounds();
669 if (master.supportsHighRes()) {
670 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
671 if (highresPreviewSize > originalBounds.width()) {
672 highresPreviewSize = originalBounds.width();
674 Rect bounds = new Rect();
675 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
676 master.getActivity(), highresPreviewSize,
677 master.getOrientation(), bounds);
678 master.setOriginalBounds(bounds);
679 master.setOriginalBitmapHighres(originalHires);
680 mBoundService.setOriginalBitmapHighres(originalHires);
681 master.warnListeners();
687 protected void onPostExecute(Boolean result) {
688 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
689 if (highresBitmap != null) {
690 float highResPreviewScale = (float) highresBitmap.getWidth()
691 / (float) MasterImage.getImage().getOriginalBounds().width();
692 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
697 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
700 public LoadBitmapTask() {
701 mBitmapSize = getScreenImageSize();
705 protected Boolean doInBackground(Uri... params) {
706 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
709 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
714 protected void onProgressUpdate(Boolean... values) {
715 super.onProgressUpdate(values);
720 mShowingTinyPlanet = true;
725 protected void onPostExecute(Boolean result) {
726 MasterImage.setMaster(mMasterImage);
733 // TODO: We should figure out the best way preventing this from
734 // happening, e.g: early checking.
738 if (null == CachingPipeline.getRenderScriptContext()){
739 Log.v(LOGTAG,"RenderScript context destroyed during load");
742 final View loading = findViewById(R.id.loading);
743 loading.setVisibility(View.GONE);
744 final View imageShow = findViewById(R.id.imageShow);
745 imageShow.setVisibility(View.VISIBLE);
747 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
748 mBoundService.setOriginalBitmap(largeBitmap);
749 MasterImage.getImage().resetGeometryImages();
751 float previewScale = (float) largeBitmap.getWidth()
752 / (float) MasterImage.getImage().getOriginalBounds().width();
753 mBoundService.setPreviewScaleFactor(previewScale);
754 if (!mShowingTinyPlanet) {
755 mCategoryFiltersAdapter.removeTinyPlanet();
757 mCategoryLooksAdapter.imageLoaded();
758 mCategoryBordersAdapter.imageLoaded();
759 mCategoryGeometryAdapter.imageLoaded();
760 mCategoryFiltersAdapter.imageLoaded();
761 mLoadBitmapTask = null;
763 if (mOriginalPreset != null) {
764 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
765 MasterImage.getImage().setPreset(mOriginalPreset,
766 mOriginalPreset.getLastRepresentation(), true);
767 mOriginalPreset = null;
770 if (mAction == TINY_PLANET_ACTION) {
771 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
773 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
774 highresLoad.execute();
775 super.onPostExecute(result);
780 private void clearGalleryBitmapPool() {
781 (new AsyncTask<Void, Void, Void>() {
783 protected Void doInBackground(Void... params) {
784 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
785 GalleryBitmapPool.getInstance().clear();
792 protected void onDestroy() {
793 if (mLoadBitmapTask != null) {
794 mLoadBitmapTask.cancel(false);
796 mUserPresetsManager.close();
801 // TODO: find a more robust way of handling image size selection
802 // for high screen densities.
803 private int getScreenImageSize() {
804 DisplayMetrics outMetrics = new DisplayMetrics();
805 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
806 return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
809 private void showSavingProgress(String albumName) {
810 ProgressDialog progress;
811 if (mSavingProgressDialog != null) {
812 progress = mSavingProgressDialog.get();
813 if (progress != null) {
818 // TODO: Allow cancellation of the saving process
820 if (albumName == null) {
821 progressText = getString(R.string.saving_image);
823 progressText = getString(R.string.filtershow_saving_image, albumName);
825 progress = ProgressDialog.show(this, "", progressText, true, false);
826 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
829 private void hideSavingProgress() {
830 if (mSavingProgressDialog != null) {
831 ProgressDialog progress = mSavingProgressDialog.get();
832 if (progress != null)
837 public void completeSaveImage(Uri saveUri) {
838 if (mSharingImage && mSharedOutputFile != null) {
839 // Image saved, we unblock the content provider
840 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
841 Uri.encode(mSharedOutputFile.getAbsolutePath()));
842 ContentValues values = new ContentValues();
843 values.put(SharedImageProvider.PREPARE, false);
844 getContentResolver().insert(uri, values);
846 setResult(RESULT_OK, new Intent().setData(saveUri));
847 hideSavingProgress();
852 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
853 // First, let's tell the SharedImageProvider that it will need to wait
855 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
856 Uri.encode(mSharedOutputFile.getAbsolutePath()));
857 ContentValues values = new ContentValues();
858 values.put(SharedImageProvider.PREPARE, true);
859 getContentResolver().insert(uri, values);
860 mSharingImage = true;
862 // Process and save the image in the background.
863 showSavingProgress(null);
864 mImageShow.saveImage(this, mSharedOutputFile);
868 private Intent getDefaultShareIntent() {
869 Intent intent = new Intent(Intent.ACTION_SEND);
870 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
871 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
872 intent.setType(SharedImageProvider.MIME_TYPE);
873 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
874 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
875 Uri.encode(mSharedOutputFile.getAbsolutePath()));
876 intent.putExtra(Intent.EXTRA_STREAM, uri);
881 public boolean onCreateOptionsMenu(Menu menu) {
882 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
883 MenuItem showState = menu.findItem(R.id.showImageStateButton);
884 if (mShowingImageStatePanel) {
885 showState.setTitle(R.string.hide_imagestate_panel);
887 showState.setTitle(R.string.show_imagestate_panel);
889 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
890 .getActionProvider();
891 mShareActionProvider.setShareIntent(getDefaultShareIntent());
892 mShareActionProvider.setOnShareTargetSelectedListener(this);
894 MenuItem undoItem = menu.findItem(R.id.undoButton);
895 MenuItem redoItem = menu.findItem(R.id.redoButton);
896 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
897 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
902 public void onPause() {
904 if (mShareActionProvider != null) {
905 mShareActionProvider.setOnShareTargetSelectedListener(null);
910 public void onResume() {
912 if (mShareActionProvider != null) {
913 mShareActionProvider.setOnShareTargetSelectedListener(this);
918 public boolean onOptionsItemSelected(MenuItem item) {
919 switch (item.getItemId()) {
920 case R.id.undoButton: {
921 HistoryManager adapter = mMasterImage.getHistory();
922 int position = adapter.undo();
923 mMasterImage.onHistoryItemClick(position);
926 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
927 UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
930 case R.id.redoButton: {
931 HistoryManager adapter = mMasterImage.getHistory();
932 int position = adapter.redo();
933 mMasterImage.onHistoryItemClick(position);
935 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
936 UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
939 case R.id.resetHistoryButton: {
941 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
942 UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
945 case R.id.showImageStateButton: {
946 toggleImageStatePanel();
947 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
948 UsageStatistics.CATEGORY_BUTTON_PRESS,
949 mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
952 case R.id.exportFlattenButton: {
953 showExportOptionsDialog();
956 case android.R.id.home: {
960 case R.id.manageUserPresets: {
964 case R.id.showInfoPanel: {
965 toggleInformationPanel();
972 public void addNewPreset() {
973 DialogFragment dialog = new PresetManagementDialog();
974 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
977 private void manageUserPresets() {
978 DialogFragment dialog = new PresetManagementDialog();
979 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
982 private void showExportOptionsDialog() {
983 DialogFragment dialog = new ExportDialog();
984 dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
987 public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
988 ArrayList<FilterUserPresetRepresentation> representations =
989 adapter.getDeletedRepresentations();
990 for (FilterUserPresetRepresentation representation : representations) {
991 deletePreset(representation.getId());
993 ArrayList<FilterUserPresetRepresentation> changedRepresentations =
994 adapter.getChangedRepresentations();
995 for (FilterUserPresetRepresentation representation : changedRepresentations) {
996 updatePreset(representation);
998 adapter.clearDeletedRepresentations();
999 adapter.clearChangedRepresentations();
1003 public void loadUserPresets() {
1004 mUserPresetsManager.load();
1005 updateUserPresetsFromManager();
1008 public void updateUserPresetsFromManager() {
1009 ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1010 if (presets == null) {
1013 if (mCategoryLooksAdapter != null) {
1016 if (presets.size() > 0) {
1017 mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1019 mUserPresetsAdapter.clear();
1020 for (int i = 0; i < presets.size(); i++) {
1021 FilterUserPresetRepresentation representation = presets.get(i);
1022 mCategoryLooksAdapter.add(
1023 new Action(this, representation, Action.FULL_VIEW, true));
1024 mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1026 if (presets.size() > 0) {
1027 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1029 mCategoryLooksAdapter.notifyDataSetChanged();
1030 mCategoryLooksAdapter.notifyDataSetInvalidated();
1033 public void saveCurrentImagePreset(String name) {
1034 mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1037 private void deletePreset(int id) {
1038 mUserPresetsManager.delete(id);
1041 private void updatePreset(FilterUserPresetRepresentation representation) {
1042 mUserPresetsManager.update(representation);
1045 public void enableSave(boolean enable) {
1046 if (mSaveButton != null) {
1047 mSaveButton.setEnabled(enable);
1051 private void fillLooks() {
1052 FiltersManager filtersManager = FiltersManager.getManager();
1053 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1055 mCategoryLooksAdapter = new CategoryAdapter(this);
1056 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1057 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1058 for (FilterRepresentation representation : filtersRepresentations) {
1059 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1061 if (mUserPresetsManager.getRepresentations() == null
1062 || mUserPresetsManager.getRepresentations().size() == 0) {
1063 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1066 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1067 if (panel != null) {
1068 if (panel instanceof MainPanel) {
1069 MainPanel mainPanel = (MainPanel) panel;
1070 mainPanel.loadCategoryLookPanel(true);
1075 public void setDefaultPreset() {
1076 // Default preset (original)
1077 ImagePreset preset = new ImagePreset(); // empty
1078 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1081 // //////////////////////////////////////////////////////////////////////////////
1082 // Some utility functions
1083 // TODO: finish the cleanup.
1085 public void invalidateViews() {
1086 for (ImageShow views : mImageViews) {
1087 views.updateImage();
1091 public void hideImageViews() {
1092 for (View view : mImageViews) {
1093 view.setVisibility(View.GONE);
1095 mEditorPlaceHolder.hide();
1098 // //////////////////////////////////////////////////////////////////////////////
1099 // imageState panel...
1101 public void toggleImageStatePanel() {
1102 invalidateOptionsMenu();
1103 mShowingImageStatePanel = !mShowingImageStatePanel;
1104 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1105 if (panel != null) {
1106 if (panel instanceof EditorPanel) {
1107 EditorPanel editorPanel = (EditorPanel) panel;
1108 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1109 } else if (panel instanceof MainPanel) {
1110 MainPanel mainPanel = (MainPanel) panel;
1111 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1116 public void toggleVersionsPanel() {
1117 mShowingVersionsPanel = !mShowingVersionsPanel;
1118 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1119 if (panel != null && panel instanceof MainPanel) {
1120 MainPanel mainPanel = (MainPanel) panel;
1121 mainPanel.loadCategoryVersionsPanel();
1126 public void onConfigurationChanged(Configuration newConfig)
1128 super.onConfigurationChanged(newConfig);
1134 // mLoadBitmapTask==null implies you have looked at the intent
1135 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1136 mCategoryFiltersAdapter.removeTinyPlanet();
1138 final View loading = findViewById(R.id.loading);
1139 loading.setVisibility(View.GONE);
1142 public void setupMasterImage() {
1144 HistoryManager historyManager = new HistoryManager();
1145 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1146 MasterImage.reset();
1147 mMasterImage = MasterImage.getImage();
1148 mMasterImage.setHistoryManager(historyManager);
1149 mMasterImage.setStateAdapter(imageStateAdapter);
1150 mMasterImage.setActivity(this);
1152 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1153 mMasterImage.setSupportsHighRes(true);
1155 mMasterImage.setSupportsHighRes(false);
1159 void resetHistory() {
1160 HistoryManager adapter = mMasterImage.getHistory();
1162 HistoryItem historyItem = adapter.getItem(0);
1163 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
1164 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
1169 public void showDefaultImageView() {
1170 hideInformationPanel();
1171 mEditorPlaceHolder.hide();
1172 mImageShow.setVisibility(View.VISIBLE);
1173 MasterImage.getImage().setCurrentFilter(null);
1174 MasterImage.getImage().setCurrentFilterRepresentation(null);
1177 public void backToMain() {
1178 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1179 if (currentPanel instanceof MainPanel) {
1183 showDefaultImageView();
1187 public void onBackPressed() {
1188 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1189 if (currentPanel instanceof MainPanel) {
1190 if (!mImageShow.hasModifications()) {
1193 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1194 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1195 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1197 public void onClick(DialogInterface dialog, int id) {
1201 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1203 public void onClick(DialogInterface dialog, int id) {
1214 public void cannotLoadImage() {
1215 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1219 // //////////////////////////////////////////////////////////////////////////////
1221 public float getPixelsFromDip(float value) {
1222 Resources r = getResources();
1223 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1224 r.getDisplayMetrics());
1228 public void onItemClick(AdapterView<?> parent, View view, int position,
1230 mMasterImage.onHistoryItemClick(position);
1234 public void pickImage() {
1235 Intent intent = new Intent();
1236 intent.setType("image/*");
1237 intent.setAction(Intent.ACTION_GET_CONTENT);
1238 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1243 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1244 if (resultCode == RESULT_OK) {
1245 if (requestCode == SELECT_PICTURE) {
1246 Uri selectedImageUri = data.getData();
1247 startLoadBitmap(selectedImageUri);
1253 public void saveImage() {
1254 if (mImageShow.hasModifications()) {
1255 // Get the name of the album, to which the image will be saved
1256 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1257 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1258 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1259 showSavingProgress(albumName);
1260 mImageShow.saveImage(this, null);
1267 public void done() {
1268 hideSavingProgress();
1269 if (mLoadBitmapTask != null) {
1270 mLoadBitmapTask.cancel(false);
1275 private void extractXMPData() {
1276 XMresults res = XmpPresets.extractXMPData(
1277 getBaseContext(), mMasterImage, getIntent().getData());
1281 mOriginalImageUri = res.originalimage;
1282 mOriginalPreset = res.preset;
1285 public Uri getSelectedImageUri() {
1286 return mSelectedImageUri;
1289 public void setHandlesSwipeForView(View view, float startX, float startY) {
1291 mHandlingSwipeButton = true;
1293 mHandlingSwipeButton = false;
1295 mHandledSwipeView = view;
1296 int[] location = new int[2];
1297 view.getLocationInWindow(location);
1298 mSwipeStartX = location[0] + startX;
1299 mSwipeStartY = location[1] + startY;
1302 public boolean dispatchTouchEvent (MotionEvent ev) {
1303 if (mHandlingSwipeButton) {
1304 int direction = CategoryView.HORIZONTAL;
1305 if (mHandledSwipeView instanceof CategoryView) {
1306 direction = ((CategoryView) mHandledSwipeView).getOrientation();
1308 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1309 float delta = ev.getY() - mSwipeStartY;
1310 float distance = mHandledSwipeView.getHeight();
1311 if (direction == CategoryView.VERTICAL) {
1312 delta = ev.getX() - mSwipeStartX;
1313 mHandledSwipeView.setTranslationX(delta);
1314 distance = mHandledSwipeView.getWidth();
1316 mHandledSwipeView.setTranslationY(delta);
1318 delta = Math.abs(delta);
1319 float transparency = Math.min(1, delta / distance);
1320 mHandledSwipeView.setAlpha(1.f - transparency);
1321 mHandledSwipeViewLastDelta = delta;
1323 if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1324 || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1325 mHandledSwipeView.setTranslationX(0);
1326 mHandledSwipeView.setTranslationY(0);
1327 mHandledSwipeView.setAlpha(1.f);
1328 mHandlingSwipeButton = false;
1329 float distance = mHandledSwipeView.getHeight();
1330 if (direction == CategoryView.VERTICAL) {
1331 distance = mHandledSwipeView.getWidth();
1333 if (mHandledSwipeViewLastDelta > distance) {
1334 ((SwipableView) mHandledSwipeView).delete();
1339 return super.dispatchTouchEvent(ev);