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.Matrix;
33 import android.graphics.Rect;
34 import android.graphics.RectF;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.AsyncTask;
38 import android.os.Bundle;
39 import android.os.CancellationSignal;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.support.v4.app.DialogFragment;
43 import android.support.v4.app.Fragment;
44 import android.support.v4.app.FragmentActivity;
45 import android.support.v4.app.FragmentTransaction;
46 import android.util.DisplayMetrics;
47 import android.util.Log;
48 import android.util.TypedValue;
49 import android.view.Menu;
50 import android.view.MenuItem;
51 import android.view.MotionEvent;
52 import android.view.View;
53 import android.view.View.OnClickListener;
54 import android.view.ViewPropertyAnimator;
55 import android.view.WindowManager;
56 import android.widget.AdapterView;
57 import android.widget.AdapterView.OnItemClickListener;
58 import android.widget.FrameLayout;
59 import android.widget.ShareActionProvider;
60 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
61 import android.widget.Toast;
63 import com.android.gallery3d.R;
64 import com.android.gallery3d.app.PhotoPage;
65 import com.android.gallery3d.data.LocalAlbum;
66 import com.android.gallery3d.filtershow.cache.ImageLoader;
67 import com.android.gallery3d.filtershow.category.Action;
68 import com.android.gallery3d.filtershow.category.CategoryAdapter;
69 import com.android.gallery3d.filtershow.category.CategoryView;
70 import com.android.gallery3d.filtershow.category.MainPanel;
71 import com.android.gallery3d.filtershow.category.SwipableView;
72 import com.android.gallery3d.filtershow.data.UserPresetsManager;
73 import com.android.gallery3d.filtershow.editors.BasicEditor;
74 import com.android.gallery3d.filtershow.editors.Editor;
75 import com.android.gallery3d.filtershow.editors.EditorChanSat;
76 import com.android.gallery3d.filtershow.editors.EditorColorBorder;
77 import com.android.gallery3d.filtershow.editors.EditorCrop;
78 import com.android.gallery3d.filtershow.editors.EditorDraw;
79 import com.android.gallery3d.filtershow.editors.EditorGrad;
80 import com.android.gallery3d.filtershow.editors.EditorManager;
81 import com.android.gallery3d.filtershow.editors.EditorMirror;
82 import com.android.gallery3d.filtershow.editors.EditorPanel;
83 import com.android.gallery3d.filtershow.editors.EditorRedEye;
84 import com.android.gallery3d.filtershow.editors.EditorRotate;
85 import com.android.gallery3d.filtershow.editors.EditorStraighten;
86 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
87 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
88 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
89 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
90 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
91 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
92 import com.android.gallery3d.filtershow.filters.FiltersManager;
93 import com.android.gallery3d.filtershow.filters.ImageFilter;
94 import com.android.gallery3d.filtershow.history.HistoryItem;
95 import com.android.gallery3d.filtershow.history.HistoryManager;
96 import com.android.gallery3d.filtershow.imageshow.ImageShow;
97 import com.android.gallery3d.filtershow.imageshow.MasterImage;
98 import com.android.gallery3d.filtershow.imageshow.Spline;
99 import com.android.gallery3d.filtershow.info.InfoPanel;
100 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
101 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
102 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
103 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
104 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
105 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
106 import com.android.gallery3d.filtershow.state.StateAdapter;
107 import com.android.gallery3d.filtershow.tools.SaveImage;
108 import com.android.gallery3d.filtershow.tools.XmpPresets;
109 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
110 import com.android.gallery3d.filtershow.ui.ExportDialog;
111 import com.android.gallery3d.filtershow.ui.FramedTextButton;
112 import com.android.gallery3d.util.GalleryUtils;
113 import com.android.gallery3d.util.PrintJob;
114 import com.android.gallery3d.util.UsageStatistics;
115 import com.android.photos.data.GalleryBitmapPool;
118 import java.io.FileDescriptor;
119 import java.io.FileOutputStream;
120 import java.lang.ref.WeakReference;
121 import java.util.ArrayList;
122 import java.util.Vector;
124 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
125 OnShareTargetSelectedListener {
127 private String mAction = "";
128 MasterImage mMasterImage = null;
130 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
132 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
133 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
134 private ImageShow mImageShow = null;
136 private View mSaveButton = null;
138 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
140 private static final int SELECT_PICTURE = 1;
141 private static final String LOGTAG = "FilterShowActivity";
143 private boolean mShowingTinyPlanet = false;
144 private boolean mShowingImageStatePanel = false;
145 private boolean mShowingVersionsPanel = false;
146 private boolean mShowingInformationPanel = false;
148 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
150 private ShareActionProvider mShareActionProvider;
151 private File mSharedOutputFile = null;
153 private boolean mSharingImage = false;
155 private WeakReference<ProgressDialog> mSavingProgressDialog;
157 private LoadBitmapTask mLoadBitmapTask;
159 private Uri mOriginalImageUri = null;
160 private ImagePreset mOriginalPreset = null;
162 private Uri mSelectedImageUri = null;
164 private UserPresetsManager mUserPresetsManager = null;
165 private UserPresetsAdapter mUserPresetsAdapter = null;
166 private CategoryAdapter mCategoryLooksAdapter = null;
167 private CategoryAdapter mCategoryBordersAdapter = null;
168 private CategoryAdapter mCategoryGeometryAdapter = null;
169 private CategoryAdapter mCategoryFiltersAdapter = null;
170 private CategoryAdapter mCategoryVersionsAdapter = null;
171 private int mCurrentPanel = MainPanel.LOOKS;
172 private Vector<FilterUserPresetRepresentation> mVersions =
173 new Vector<FilterUserPresetRepresentation>();
174 private int mVersionsCounter = 0;
176 private boolean mHandlingSwipeButton = false;
177 private View mHandledSwipeView = null;
178 private float mHandledSwipeViewLastDelta = 0;
179 private float mSwipeStartX = 0;
180 private float mSwipeStartY = 0;
182 private ProcessingService mBoundService;
183 private boolean mIsBound = false;
185 public ProcessingService getProcessingService() {
186 return mBoundService;
189 public boolean isSimpleEditAction() {
190 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
193 private ServiceConnection mConnection = new ServiceConnection() {
195 public void onServiceConnected(ComponentName className, IBinder service) {
197 * This is called when the connection with the service has been
198 * established, giving us the service object we can use to
199 * interact with the service. Because we have bound to a explicit
200 * service that we know is running in our own process, we can
201 * cast its IBinder to a concrete class and directly access it.
203 mBoundService = ((ProcessingService.LocalBinder)service).getService();
204 mBoundService.setFiltershowActivity(FilterShowActivity.this);
205 mBoundService.onStart();
209 public void onServiceDisconnected(ComponentName className) {
211 * This is called when the connection with the service has been
212 * unexpectedly disconnected -- that is, its process crashed.
213 * Because it is running in our same process, we should never
216 mBoundService = null;
220 void doBindService() {
222 * Establish a connection with the service. We use an explicit
223 * class name because we want a specific service implementation that
224 * we know will be running in our own process (and thus won't be
225 * supporting component replacement by other applications).
227 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
228 mConnection, Context.BIND_AUTO_CREATE);
232 void doUnbindService() {
234 // Detach our existing connection.
235 unbindService(mConnection);
240 private void setupPipeline() {
242 ImageFilter.setActivityForMemoryToasts(this);
243 mUserPresetsManager = new UserPresetsManager(this);
244 mUserPresetsAdapter = new UserPresetsAdapter(this);
247 public void updateUIAfterServiceStarted() {
256 public void onCreate(Bundle savedInstanceState) {
257 super.onCreate(savedInstanceState);
259 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
260 if (onlyUsePortrait) {
261 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
263 MasterImage.setMaster(mMasterImage);
265 clearGalleryBitmapPool();
273 UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
274 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
275 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
278 public boolean isShowingImageStatePanel() {
279 return mShowingImageStatePanel;
282 public void loadMainPanel() {
283 if (findViewById(R.id.main_panel_container) == null) {
286 MainPanel panel = new MainPanel();
287 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
288 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
289 transaction.commit();
292 public void loadEditorPanel(FilterRepresentation representation,
293 final Editor currentEditor) {
294 if (representation.getEditorId() == ImageOnlyEditor.ID) {
295 currentEditor.reflectCurrentFilter();
298 final int currentId = currentEditor.getID();
299 Runnable showEditor = new Runnable() {
302 EditorPanel panel = new EditorPanel();
303 panel.setEditor(currentId);
304 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
305 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
306 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
307 transaction.commit();
310 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
311 boolean doAnimation = false;
312 if (mShowingImageStatePanel
313 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
316 if (doAnimation && main != null && main instanceof MainPanel) {
317 MainPanel mainPanel = (MainPanel) main;
318 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
319 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
320 int panelHeight = container.getHeight() + bottom.getHeight();
321 ViewPropertyAnimator anim = mainPanel.getView().animate();
322 anim.translationY(panelHeight).start();
323 final Handler handler = new Handler();
324 handler.postDelayed(showEditor, anim.getDuration());
330 public void hideInformationPanel() {
331 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
332 infoLayout.setVisibility(View.GONE);
333 Fragment fragment = getSupportFragmentManager().findFragmentByTag(InfoPanel.FRAGMENT_TAG);
334 if (fragment != null) {
335 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
336 transaction.remove(fragment);
337 transaction.commit();
339 mShowingInformationPanel = false;
342 public void toggleInformationPanel() {
343 mShowingInformationPanel = !mShowingInformationPanel;
344 if (!mShowingInformationPanel) {
345 hideInformationPanel();
346 showDefaultImageView();
349 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
350 transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
351 FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
352 infoLayout.setVisibility(View.VISIBLE);
353 mEditorPlaceHolder.hide();
354 mImageShow.setVisibility(View.GONE);
356 InfoPanel panel = new InfoPanel();
357 transaction.replace(R.id.central_panel_container, panel, InfoPanel.FRAGMENT_TAG);
358 transaction.commit();
361 private void loadXML() {
362 setContentView(R.layout.filtershow_activity);
364 ActionBar actionBar = getActionBar();
365 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
366 actionBar.setCustomView(R.layout.filtershow_actionbar);
368 mSaveButton = actionBar.getCustomView();
369 mSaveButton.setOnClickListener(new OnClickListener() {
371 public void onClick(View view) {
376 mImageShow = (ImageShow) findViewById(R.id.imageShow);
377 mImageViews.add(mImageShow);
381 mEditorPlaceHolder.hide();
382 mImageShow.bindAsImageLoadListener();
387 public void fillCategories() {
396 public void setupStatePanel() {
397 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
400 private void fillVersions() {
401 mCategoryVersionsAdapter = new CategoryAdapter(this);
402 mCategoryVersionsAdapter.setShowAddButton(true);
405 public void updateVersions() {
406 mCategoryVersionsAdapter.clear();
407 FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
408 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
409 mCategoryVersionsAdapter.add(
410 new Action(this, originalRep, Action.FULL_VIEW));
411 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
412 FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
413 getString(R.string.filtershow_version_current), current, -1);
414 mCategoryVersionsAdapter.add(
415 new Action(this, currentRep, Action.FULL_VIEW));
416 if (mVersions.size() > 0) {
417 mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
419 for (FilterUserPresetRepresentation rep : mVersions) {
420 mCategoryVersionsAdapter.add(
421 new Action(this, rep, Action.FULL_VIEW, true));
423 mCategoryVersionsAdapter.notifyDataSetInvalidated();
426 public void addCurrentVersion() {
427 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
429 FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
430 "" + mVersionsCounter, current, -1);
435 public void removeVersion(Action action) {
436 mVersions.remove(action.getRepresentation());
440 public void removeLook(Action action) {
441 FilterUserPresetRepresentation rep =
442 (FilterUserPresetRepresentation) action.getRepresentation();
446 mUserPresetsManager.delete(rep.getId());
447 updateUserPresetsFromManager();
450 private void fillEffects() {
451 FiltersManager filtersManager = FiltersManager.getManager();
452 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
453 mCategoryFiltersAdapter = new CategoryAdapter(this);
454 for (FilterRepresentation representation : filtersRepresentations) {
455 if (representation.getTextId() != 0) {
456 representation.setName(getString(representation.getTextId()));
458 mCategoryFiltersAdapter.add(new Action(this, representation));
462 private void fillTools() {
463 FiltersManager filtersManager = FiltersManager.getManager();
464 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
465 mCategoryGeometryAdapter = new CategoryAdapter(this);
466 for (FilterRepresentation representation : filtersRepresentations) {
467 mCategoryGeometryAdapter.add(new Action(this, representation));
471 private void processIntent() {
472 Intent intent = getIntent();
473 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
474 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
477 mAction = intent.getAction();
478 mSelectedImageUri = intent.getData();
479 Uri loadUri = mSelectedImageUri;
480 if (mOriginalImageUri != null) {
481 loadUri = mOriginalImageUri;
483 if (loadUri != null) {
484 startLoadBitmap(loadUri);
490 private void setupEditors() {
491 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
492 EditorManager.addEditors(mEditorPlaceHolder);
493 mEditorPlaceHolder.setOldViews(mImageViews);
496 private void fillEditors() {
497 mEditorPlaceHolder.addEditor(new EditorChanSat());
498 mEditorPlaceHolder.addEditor(new EditorGrad());
499 mEditorPlaceHolder.addEditor(new EditorDraw());
500 mEditorPlaceHolder.addEditor(new EditorColorBorder());
501 mEditorPlaceHolder.addEditor(new BasicEditor());
502 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
503 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
504 mEditorPlaceHolder.addEditor(new EditorRedEye());
505 mEditorPlaceHolder.addEditor(new EditorCrop());
506 mEditorPlaceHolder.addEditor(new EditorMirror());
507 mEditorPlaceHolder.addEditor(new EditorRotate());
508 mEditorPlaceHolder.addEditor(new EditorStraighten());
511 private void setDefaultValues() {
512 Resources res = getResources();
514 // TODO: get those values from XML.
515 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
516 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
517 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
519 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
520 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
521 Spline.setCurveHandle(curveHandle, curveHandleSize);
522 Spline.setCurveWidth((int) getPixelsFromDip(3));
525 private void startLoadBitmap(Uri uri) {
526 final View loading = findViewById(R.id.loading);
527 final View imageShow = findViewById(R.id.imageShow);
528 imageShow.setVisibility(View.INVISIBLE);
529 loading.setVisibility(View.VISIBLE);
530 mShowingTinyPlanet = false;
531 mLoadBitmapTask = new LoadBitmapTask();
532 mLoadBitmapTask.execute(uri);
535 private void fillBorders() {
536 FiltersManager filtersManager = FiltersManager.getManager();
537 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
539 for (int i = 0; i < borders.size(); i++) {
540 FilterRepresentation filter = borders.get(i);
541 filter.setName(getString(R.string.borders));
543 filter.setName(getString(R.string.none));
547 mCategoryBordersAdapter = new CategoryAdapter(this);
548 for (FilterRepresentation representation : borders) {
549 if (representation.getTextId() != 0) {
550 representation.setName(getString(representation.getTextId()));
552 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
556 public UserPresetsAdapter getUserPresetsAdapter() {
557 return mUserPresetsAdapter;
560 public CategoryAdapter getCategoryLooksAdapter() {
561 return mCategoryLooksAdapter;
564 public CategoryAdapter getCategoryBordersAdapter() {
565 return mCategoryBordersAdapter;
568 public CategoryAdapter getCategoryGeometryAdapter() {
569 return mCategoryGeometryAdapter;
572 public CategoryAdapter getCategoryFiltersAdapter() {
573 return mCategoryFiltersAdapter;
576 public CategoryAdapter getCategoryVersionsAdapter() {
577 return mCategoryVersionsAdapter;
580 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
581 if (filterRepresentation == null) {
584 ImagePreset oldPreset = MasterImage.getImage().getPreset();
585 ImagePreset copy = new ImagePreset(oldPreset);
586 copy.removeFilter(filterRepresentation);
587 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
588 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
589 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
590 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
594 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
595 if (filterRepresentation == null) {
598 if (!(filterRepresentation instanceof FilterRotateRepresentation)
599 && !(filterRepresentation instanceof FilterMirrorRepresentation)
600 && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
603 ImagePreset oldPreset = MasterImage.getImage().getPreset();
604 ImagePreset copy = new ImagePreset(oldPreset);
605 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
606 if (representation == null) {
607 filterRepresentation = filterRepresentation.copy();
608 copy.addFilter(filterRepresentation);
610 if (filterRepresentation.allowsSingleInstanceOnly()) {
611 // Don't just update the filter representation. Centralize the
612 // logic in the addFilter(), such that we can keep "None" as
614 if (!representation.equals(filterRepresentation)) {
615 // Only do this if the filter isn't the same
616 // (state panel clicks can lead us here)
617 copy.removeFilter(representation);
618 copy.addFilter(filterRepresentation);
622 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
623 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
626 public void showRepresentation(FilterRepresentation representation) {
627 if (representation == null) {
631 if (representation instanceof FilterRotateRepresentation) {
632 FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
635 if (representation instanceof FilterMirrorRepresentation) {
636 FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
639 useFilterRepresentation(representation);
641 // show representation
642 Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
643 loadEditorPanel(representation, mCurrentEditor);
644 hideInformationPanel();
647 public Editor getEditor(int editorID) {
648 return mEditorPlaceHolder.getEditor(editorID);
651 public void setCurrentPanel(int currentPanel) {
652 mCurrentPanel = currentPanel;
655 public int getCurrentPanel() {
656 return mCurrentPanel;
659 public void updateCategories() {
660 ImagePreset preset = mMasterImage.getPreset();
661 mCategoryLooksAdapter.reflectImagePreset(preset);
662 mCategoryBordersAdapter.reflectImagePreset(preset);
665 public View getMainStatePanelContainer(int id) {
666 return findViewById(id);
669 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
671 protected Boolean doInBackground(Void... params) {
672 MasterImage master = MasterImage.getImage();
673 Rect originalBounds = master.getOriginalBounds();
674 if (master.supportsHighRes()) {
675 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
676 if (highresPreviewSize > originalBounds.width()) {
677 highresPreviewSize = originalBounds.width();
679 Rect bounds = new Rect();
680 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
681 master.getActivity(), highresPreviewSize,
682 master.getOrientation(), bounds);
683 master.setOriginalBounds(bounds);
684 master.setOriginalBitmapHighres(originalHires);
685 mBoundService.setOriginalBitmapHighres(originalHires);
686 master.warnListeners();
692 protected void onPostExecute(Boolean result) {
693 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
694 if (highresBitmap != null) {
695 float highResPreviewScale = (float) highresBitmap.getWidth()
696 / (float) MasterImage.getImage().getOriginalBounds().width();
697 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
702 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
705 public LoadBitmapTask() {
706 mBitmapSize = getScreenImageSize();
710 protected Boolean doInBackground(Uri... params) {
711 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
714 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
719 protected void onProgressUpdate(Boolean... values) {
720 super.onProgressUpdate(values);
725 mShowingTinyPlanet = true;
730 protected void onPostExecute(Boolean result) {
731 MasterImage.setMaster(mMasterImage);
737 if (!mOriginalImageUri.equals(mSelectedImageUri)) {
738 mOriginalImageUri = mSelectedImageUri;
739 mOriginalPreset = null;
740 Toast.makeText(FilterShowActivity.this,
741 R.string.cannot_edit_original, Toast.LENGTH_SHORT).show();
742 startLoadBitmap(mOriginalImageUri);
749 if (null == CachingPipeline.getRenderScriptContext()){
750 Log.v(LOGTAG,"RenderScript context destroyed during load");
753 final View loading = findViewById(R.id.loading);
754 loading.setVisibility(View.GONE);
755 final View imageShow = findViewById(R.id.imageShow);
756 imageShow.setVisibility(View.VISIBLE);
758 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
759 mBoundService.setOriginalBitmap(largeBitmap);
760 MasterImage.getImage().resetGeometryImages(true);
762 float previewScale = (float) largeBitmap.getWidth()
763 / (float) MasterImage.getImage().getOriginalBounds().width();
764 mBoundService.setPreviewScaleFactor(previewScale);
765 if (!mShowingTinyPlanet) {
766 mCategoryFiltersAdapter.removeTinyPlanet();
768 mCategoryLooksAdapter.imageLoaded();
769 mCategoryBordersAdapter.imageLoaded();
770 mCategoryGeometryAdapter.imageLoaded();
771 mCategoryFiltersAdapter.imageLoaded();
772 mLoadBitmapTask = null;
774 if (mOriginalPreset != null) {
775 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
776 MasterImage.getImage().setPreset(mOriginalPreset,
777 mOriginalPreset.getLastRepresentation(), true);
778 mOriginalPreset = null;
781 if (mAction == TINY_PLANET_ACTION) {
782 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
784 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
785 highresLoad.execute();
786 super.onPostExecute(result);
791 private void clearGalleryBitmapPool() {
792 (new AsyncTask<Void, Void, Void>() {
794 protected Void doInBackground(Void... params) {
795 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
796 GalleryBitmapPool.getInstance().clear();
803 protected void onDestroy() {
804 if (mLoadBitmapTask != null) {
805 mLoadBitmapTask.cancel(false);
807 mUserPresetsManager.close();
812 // TODO: find a more robust way of handling image size selection
813 // for high screen densities.
814 private int getScreenImageSize() {
815 DisplayMetrics outMetrics = new DisplayMetrics();
816 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
817 return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
820 private void showSavingProgress(String albumName) {
821 ProgressDialog progress;
822 if (mSavingProgressDialog != null) {
823 progress = mSavingProgressDialog.get();
824 if (progress != null) {
829 // TODO: Allow cancellation of the saving process
831 if (albumName == null) {
832 progressText = getString(R.string.saving_image);
834 progressText = getString(R.string.filtershow_saving_image, albumName);
836 progress = ProgressDialog.show(this, "", progressText, true, false);
837 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
840 private void hideSavingProgress() {
841 if (mSavingProgressDialog != null) {
842 ProgressDialog progress = mSavingProgressDialog.get();
843 if (progress != null)
848 public void completeSaveImage(Uri saveUri) {
849 if (mSharingImage && mSharedOutputFile != null) {
850 // Image saved, we unblock the content provider
851 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
852 Uri.encode(mSharedOutputFile.getAbsolutePath()));
853 ContentValues values = new ContentValues();
854 values.put(SharedImageProvider.PREPARE, false);
855 getContentResolver().insert(uri, values);
857 setResult(RESULT_OK, new Intent().setData(saveUri));
858 hideSavingProgress();
863 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
864 // First, let's tell the SharedImageProvider that it will need to wait
866 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
867 Uri.encode(mSharedOutputFile.getAbsolutePath()));
868 ContentValues values = new ContentValues();
869 values.put(SharedImageProvider.PREPARE, true);
870 getContentResolver().insert(uri, values);
871 mSharingImage = true;
873 // Process and save the image in the background.
874 showSavingProgress(null);
875 mImageShow.saveImage(this, mSharedOutputFile);
879 private Intent getDefaultShareIntent() {
880 Intent intent = new Intent(Intent.ACTION_SEND);
881 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
882 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
883 intent.setType(SharedImageProvider.MIME_TYPE);
884 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
885 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
886 Uri.encode(mSharedOutputFile.getAbsolutePath()));
887 intent.putExtra(Intent.EXTRA_STREAM, uri);
892 public boolean onCreateOptionsMenu(Menu menu) {
893 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
894 MenuItem showState = menu.findItem(R.id.showImageStateButton);
895 if (mShowingImageStatePanel) {
896 showState.setTitle(R.string.hide_imagestate_panel);
898 showState.setTitle(R.string.show_imagestate_panel);
900 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
901 .getActionProvider();
902 mShareActionProvider.setShareIntent(getDefaultShareIntent());
903 mShareActionProvider.setOnShareTargetSelectedListener(this);
905 MenuItem undoItem = menu.findItem(R.id.undoButton);
906 MenuItem redoItem = menu.findItem(R.id.redoButton);
907 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
908 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
913 public void onPause() {
915 if (mShareActionProvider != null) {
916 mShareActionProvider.setOnShareTargetSelectedListener(null);
921 public void onResume() {
923 if (mShareActionProvider != null) {
924 mShareActionProvider.setOnShareTargetSelectedListener(this);
929 public boolean onOptionsItemSelected(MenuItem item) {
930 switch (item.getItemId()) {
931 case R.id.undoButton: {
932 HistoryManager adapter = mMasterImage.getHistory();
933 int position = adapter.undo();
934 mMasterImage.onHistoryItemClick(position);
937 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
938 UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
941 case R.id.redoButton: {
942 HistoryManager adapter = mMasterImage.getHistory();
943 int position = adapter.redo();
944 mMasterImage.onHistoryItemClick(position);
946 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
947 UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
950 case R.id.resetHistoryButton: {
952 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
953 UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
956 case R.id.showImageStateButton: {
957 toggleImageStatePanel();
958 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
959 UsageStatistics.CATEGORY_BUTTON_PRESS,
960 mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
963 case R.id.exportFlattenButton: {
964 showExportOptionsDialog();
967 case android.R.id.home: {
971 case R.id.manageUserPresets: {
975 case R.id.showInfoPanel: {
976 toggleInformationPanel();
979 case R.id.printButton: {
987 public void print() {
988 Bitmap bitmap = MasterImage.getImage().getHighresImage();
989 PrintJob.printBitmap(this, "ImagePrint", bitmap);
992 public void addNewPreset() {
993 DialogFragment dialog = new PresetManagementDialog();
994 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
997 private void manageUserPresets() {
998 DialogFragment dialog = new PresetManagementDialog();
999 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1002 private void showExportOptionsDialog() {
1003 DialogFragment dialog = new ExportDialog();
1004 dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
1007 public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
1008 ArrayList<FilterUserPresetRepresentation> representations =
1009 adapter.getDeletedRepresentations();
1010 for (FilterUserPresetRepresentation representation : representations) {
1011 deletePreset(representation.getId());
1013 ArrayList<FilterUserPresetRepresentation> changedRepresentations =
1014 adapter.getChangedRepresentations();
1015 for (FilterUserPresetRepresentation representation : changedRepresentations) {
1016 updatePreset(representation);
1018 adapter.clearDeletedRepresentations();
1019 adapter.clearChangedRepresentations();
1023 public void loadUserPresets() {
1024 mUserPresetsManager.load();
1025 updateUserPresetsFromManager();
1028 public void updateUserPresetsFromManager() {
1029 ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1030 if (presets == null) {
1033 if (mCategoryLooksAdapter != null) {
1036 if (presets.size() > 0) {
1037 mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1039 mUserPresetsAdapter.clear();
1040 for (int i = 0; i < presets.size(); i++) {
1041 FilterUserPresetRepresentation representation = presets.get(i);
1042 mCategoryLooksAdapter.add(
1043 new Action(this, representation, Action.FULL_VIEW, true));
1044 mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1046 if (presets.size() > 0) {
1047 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1049 mCategoryLooksAdapter.notifyDataSetChanged();
1050 mCategoryLooksAdapter.notifyDataSetInvalidated();
1053 public void saveCurrentImagePreset(String name) {
1054 mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1057 private void deletePreset(int id) {
1058 mUserPresetsManager.delete(id);
1061 private void updatePreset(FilterUserPresetRepresentation representation) {
1062 mUserPresetsManager.update(representation);
1065 public void enableSave(boolean enable) {
1066 if (mSaveButton != null) {
1067 mSaveButton.setEnabled(enable);
1071 private void fillLooks() {
1072 FiltersManager filtersManager = FiltersManager.getManager();
1073 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1075 mCategoryLooksAdapter = new CategoryAdapter(this);
1076 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1077 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1078 for (FilterRepresentation representation : filtersRepresentations) {
1079 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1081 if (mUserPresetsManager.getRepresentations() == null
1082 || mUserPresetsManager.getRepresentations().size() == 0) {
1083 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1086 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1087 if (panel != null) {
1088 if (panel instanceof MainPanel) {
1089 MainPanel mainPanel = (MainPanel) panel;
1090 mainPanel.loadCategoryLookPanel(true);
1095 public void setDefaultPreset() {
1096 // Default preset (original)
1097 ImagePreset preset = new ImagePreset(); // empty
1098 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1101 // //////////////////////////////////////////////////////////////////////////////
1102 // Some utility functions
1103 // TODO: finish the cleanup.
1105 public void invalidateViews() {
1106 for (ImageShow views : mImageViews) {
1107 views.updateImage();
1111 public void hideImageViews() {
1112 for (View view : mImageViews) {
1113 view.setVisibility(View.GONE);
1115 mEditorPlaceHolder.hide();
1118 // //////////////////////////////////////////////////////////////////////////////
1119 // imageState panel...
1121 public void toggleImageStatePanel() {
1122 invalidateOptionsMenu();
1123 mShowingImageStatePanel = !mShowingImageStatePanel;
1124 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1125 if (panel != null) {
1126 if (panel instanceof EditorPanel) {
1127 EditorPanel editorPanel = (EditorPanel) panel;
1128 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1129 } else if (panel instanceof MainPanel) {
1130 MainPanel mainPanel = (MainPanel) panel;
1131 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1136 public void toggleVersionsPanel() {
1137 mShowingVersionsPanel = !mShowingVersionsPanel;
1138 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1139 if (panel != null && panel instanceof MainPanel) {
1140 MainPanel mainPanel = (MainPanel) panel;
1141 mainPanel.loadCategoryVersionsPanel();
1146 public void onConfigurationChanged(Configuration newConfig)
1148 super.onConfigurationChanged(newConfig);
1154 // mLoadBitmapTask==null implies you have looked at the intent
1155 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1156 mCategoryFiltersAdapter.removeTinyPlanet();
1158 final View loading = findViewById(R.id.loading);
1159 loading.setVisibility(View.GONE);
1162 public void setupMasterImage() {
1164 HistoryManager historyManager = new HistoryManager();
1165 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1166 MasterImage.reset();
1167 mMasterImage = MasterImage.getImage();
1168 mMasterImage.setHistoryManager(historyManager);
1169 mMasterImage.setStateAdapter(imageStateAdapter);
1170 mMasterImage.setActivity(this);
1172 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1173 mMasterImage.setSupportsHighRes(true);
1175 mMasterImage.setSupportsHighRes(false);
1179 void resetHistory() {
1180 HistoryManager adapter = mMasterImage.getHistory();
1182 HistoryItem historyItem = adapter.getItem(0);
1183 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
1184 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
1189 public void showDefaultImageView() {
1190 hideInformationPanel();
1191 mEditorPlaceHolder.hide();
1192 mImageShow.setVisibility(View.VISIBLE);
1193 MasterImage.getImage().setCurrentFilter(null);
1194 MasterImage.getImage().setCurrentFilterRepresentation(null);
1197 public void backToMain() {
1198 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1199 if (currentPanel instanceof MainPanel) {
1203 showDefaultImageView();
1207 public void onBackPressed() {
1208 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1209 if (currentPanel instanceof MainPanel) {
1210 if (!mImageShow.hasModifications()) {
1213 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1214 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1215 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1217 public void onClick(DialogInterface dialog, int id) {
1221 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1223 public void onClick(DialogInterface dialog, int id) {
1234 public void cannotLoadImage() {
1235 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1239 // //////////////////////////////////////////////////////////////////////////////
1241 public float getPixelsFromDip(float value) {
1242 Resources r = getResources();
1243 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1244 r.getDisplayMetrics());
1248 public void onItemClick(AdapterView<?> parent, View view, int position,
1250 mMasterImage.onHistoryItemClick(position);
1254 public void pickImage() {
1255 Intent intent = new Intent();
1256 intent.setType("image/*");
1257 intent.setAction(Intent.ACTION_GET_CONTENT);
1258 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1263 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1264 if (resultCode == RESULT_OK) {
1265 if (requestCode == SELECT_PICTURE) {
1266 Uri selectedImageUri = data.getData();
1267 startLoadBitmap(selectedImageUri);
1273 public void saveImage() {
1274 if (mImageShow.hasModifications()) {
1275 // Get the name of the album, to which the image will be saved
1276 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1277 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1278 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1279 showSavingProgress(albumName);
1280 mImageShow.saveImage(this, null);
1287 public void done() {
1288 hideSavingProgress();
1289 if (mLoadBitmapTask != null) {
1290 mLoadBitmapTask.cancel(false);
1295 private void extractXMPData() {
1296 XMresults res = XmpPresets.extractXMPData(
1297 getBaseContext(), mMasterImage, getIntent().getData());
1301 mOriginalImageUri = res.originalimage;
1302 mOriginalPreset = res.preset;
1305 public Uri getSelectedImageUri() {
1306 return mSelectedImageUri;
1309 public void setHandlesSwipeForView(View view, float startX, float startY) {
1311 mHandlingSwipeButton = true;
1313 mHandlingSwipeButton = false;
1315 mHandledSwipeView = view;
1316 int[] location = new int[2];
1317 view.getLocationInWindow(location);
1318 mSwipeStartX = location[0] + startX;
1319 mSwipeStartY = location[1] + startY;
1322 public boolean dispatchTouchEvent (MotionEvent ev) {
1323 if (mHandlingSwipeButton) {
1324 int direction = CategoryView.HORIZONTAL;
1325 if (mHandledSwipeView instanceof CategoryView) {
1326 direction = ((CategoryView) mHandledSwipeView).getOrientation();
1328 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1329 float delta = ev.getY() - mSwipeStartY;
1330 float distance = mHandledSwipeView.getHeight();
1331 if (direction == CategoryView.VERTICAL) {
1332 delta = ev.getX() - mSwipeStartX;
1333 mHandledSwipeView.setTranslationX(delta);
1334 distance = mHandledSwipeView.getWidth();
1336 mHandledSwipeView.setTranslationY(delta);
1338 delta = Math.abs(delta);
1339 float transparency = Math.min(1, delta / distance);
1340 mHandledSwipeView.setAlpha(1.f - transparency);
1341 mHandledSwipeViewLastDelta = delta;
1343 if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1344 || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1345 mHandledSwipeView.setTranslationX(0);
1346 mHandledSwipeView.setTranslationY(0);
1347 mHandledSwipeView.setAlpha(1.f);
1348 mHandlingSwipeButton = false;
1349 float distance = mHandledSwipeView.getHeight();
1350 if (direction == CategoryView.VERTICAL) {
1351 distance = mHandledSwipeView.getWidth();
1353 if (mHandledSwipeViewLastDelta > distance) {
1354 ((SwipableView) mHandledSwipeView).delete();
1359 return super.dispatchTouchEvent(ev);