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.Fragment;
40 import android.support.v4.app.FragmentActivity;
41 import android.support.v4.app.FragmentTransaction;
42 import android.util.DisplayMetrics;
43 import android.util.Log;
44 import android.util.TypedValue;
45 import android.view.Menu;
46 import android.view.MenuItem;
47 import android.view.View;
48 import android.view.View.OnClickListener;
49 import android.view.ViewPropertyAnimator;
50 import android.view.WindowManager;
51 import android.widget.AdapterView;
52 import android.widget.AdapterView.OnItemClickListener;
53 import android.widget.FrameLayout;
54 import android.widget.ShareActionProvider;
55 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
56 import android.widget.Toast;
58 import com.android.gallery3d.R;
59 import com.android.gallery3d.app.PhotoPage;
60 import com.android.gallery3d.data.LocalAlbum;
61 import com.android.gallery3d.filtershow.editors.EditorGrad;
62 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
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.editors.BasicEditor;
68 import com.android.gallery3d.filtershow.editors.Editor;
69 import com.android.gallery3d.filtershow.editors.EditorCrop;
70 import com.android.gallery3d.filtershow.editors.EditorDraw;
71 import com.android.gallery3d.filtershow.editors.EditorFlip;
72 import com.android.gallery3d.filtershow.editors.EditorManager;
73 import com.android.gallery3d.filtershow.editors.EditorPanel;
74 import com.android.gallery3d.filtershow.editors.EditorRedEye;
75 import com.android.gallery3d.filtershow.editors.EditorRotate;
76 import com.android.gallery3d.filtershow.editors.EditorStraighten;
77 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
78 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
79 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
80 import com.android.gallery3d.filtershow.filters.FiltersManager;
81 import com.android.gallery3d.filtershow.filters.ImageFilter;
82 import com.android.gallery3d.filtershow.history.HistoryManager;
83 import com.android.gallery3d.filtershow.history.HistoryItem;
84 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
85 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
86 import com.android.gallery3d.filtershow.imageshow.ImageShow;
87 import com.android.gallery3d.filtershow.imageshow.MasterImage;
88 import com.android.gallery3d.filtershow.imageshow.Spline;
89 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
90 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
91 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
92 import com.android.gallery3d.filtershow.state.StateAdapter;
93 import com.android.gallery3d.filtershow.tools.SaveImage;
94 import com.android.gallery3d.filtershow.tools.XmpPresets;
95 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
96 import com.android.gallery3d.filtershow.ui.FramedTextButton;
97 import com.android.gallery3d.util.GalleryUtils;
98 import com.android.gallery3d.util.UsageStatistics;
99 import com.android.photos.data.GalleryBitmapPool;
102 import java.lang.ref.WeakReference;
103 import java.util.ArrayList;
104 import java.util.Vector;
106 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
107 OnShareTargetSelectedListener {
109 private String mAction = "";
110 MasterImage mMasterImage = null;
112 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
114 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
115 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
116 private ImageShow mImageShow = null;
118 private View mSaveButton = null;
120 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
122 private static final int SELECT_PICTURE = 1;
123 private static final String LOGTAG = "FilterShowActivity";
124 protected static final boolean ANIMATE_PANELS = true;
126 private boolean mShowingTinyPlanet = false;
127 private boolean mShowingImageStatePanel = false;
129 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
131 private ShareActionProvider mShareActionProvider;
132 private File mSharedOutputFile = null;
134 private boolean mSharingImage = false;
136 private WeakReference<ProgressDialog> mSavingProgressDialog;
138 private LoadBitmapTask mLoadBitmapTask;
139 private boolean mLoading = true;
141 private Uri mOriginalImageUri = null;
142 private ImagePreset mOriginalPreset = null;
144 private Uri mSelectedImageUri = null;
146 private CategoryAdapter mCategoryLooksAdapter = null;
147 private CategoryAdapter mCategoryBordersAdapter = null;
148 private CategoryAdapter mCategoryGeometryAdapter = null;
149 private CategoryAdapter mCategoryFiltersAdapter = null;
150 private int mCurrentPanel = MainPanel.LOOKS;
152 private ProcessingService mBoundService;
153 private boolean mIsBound = false;
155 public ProcessingService getProcessingService() {
156 return mBoundService;
159 public boolean isSimpleEditAction() {
160 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
163 private ServiceConnection mConnection = new ServiceConnection() {
164 public void onServiceConnected(ComponentName className, IBinder service) {
166 * This is called when the connection with the service has been
167 * established, giving us the service object we can use to
168 * interact with the service. Because we have bound to a explicit
169 * service that we know is running in our own process, we can
170 * cast its IBinder to a concrete class and directly access it.
172 mBoundService = ((ProcessingService.LocalBinder)service).getService();
173 mBoundService.setFiltershowActivity(FilterShowActivity.this);
174 mBoundService.onStart();
177 public void onServiceDisconnected(ComponentName className) {
179 * This is called when the connection with the service has been
180 * unexpectedly disconnected -- that is, its process crashed.
181 * Because it is running in our same process, we should never
184 mBoundService = null;
188 void doBindService() {
190 * Establish a connection with the service. We use an explicit
191 * class name because we want a specific service implementation that
192 * we know will be running in our own process (and thus won't be
193 * supporting component replacement by other applications).
195 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
196 mConnection, Context.BIND_AUTO_CREATE);
200 void doUnbindService() {
202 // Detach our existing connection.
203 unbindService(mConnection);
208 private void setupPipeline() {
210 ImageFilter.setActivityForMemoryToasts(this);
213 public void updateUIAfterServiceStarted() {
222 public void onCreate(Bundle savedInstanceState) {
223 super.onCreate(savedInstanceState);
225 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
226 if (onlyUsePortrait) {
227 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
229 MasterImage.setMaster(mMasterImage);
231 clearGalleryBitmapPool();
239 UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
240 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
241 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
244 public boolean isShowingImageStatePanel() {
245 return mShowingImageStatePanel;
248 public void loadMainPanel() {
249 if (findViewById(R.id.main_panel_container) == null) {
252 MainPanel panel = new MainPanel();
253 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
254 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
255 transaction.commit();
258 public void loadEditorPanel(FilterRepresentation representation,
259 final Editor currentEditor) {
260 if (representation.getEditorId() == ImageOnlyEditor.ID) {
261 currentEditor.reflectCurrentFilter();
264 final int currentId = currentEditor.getID();
265 Runnable showEditor = new Runnable() {
268 EditorPanel panel = new EditorPanel();
269 panel.setEditor(currentId);
270 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
271 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
272 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
273 transaction.commit();
276 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
277 boolean doAnimation = false;
278 if (mShowingImageStatePanel
279 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
282 if (doAnimation && main != null && main instanceof MainPanel) {
283 MainPanel mainPanel = (MainPanel) main;
284 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
285 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
286 int panelHeight = container.getHeight() + bottom.getHeight();
287 ViewPropertyAnimator anim = mainPanel.getView().animate();
288 anim.translationY(panelHeight).start();
289 final Handler handler = new Handler();
290 handler.postDelayed(showEditor, anim.getDuration());
296 private void loadXML() {
297 setContentView(R.layout.filtershow_activity);
299 ActionBar actionBar = getActionBar();
300 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
301 actionBar.setCustomView(R.layout.filtershow_actionbar);
303 mSaveButton = actionBar.getCustomView();
304 mSaveButton.setOnClickListener(new OnClickListener() {
306 public void onClick(View view) {
311 mImageShow = (ImageShow) findViewById(R.id.imageShow);
312 mImageViews.add(mImageShow);
316 mEditorPlaceHolder.hide();
317 mImageShow.bindAsImageLoadListener();
322 public void fillCategories() {
329 public void setupStatePanel() {
330 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
333 private void fillEffects() {
334 FiltersManager filtersManager = FiltersManager.getManager();
335 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
336 mCategoryFiltersAdapter = new CategoryAdapter(this);
337 for (FilterRepresentation representation : filtersRepresentations) {
338 if (representation.getTextId() != 0) {
339 representation.setName(getString(representation.getTextId()));
341 mCategoryFiltersAdapter.add(new Action(this, representation));
345 private void fillTools() {
346 FiltersManager filtersManager = FiltersManager.getManager();
347 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
348 mCategoryGeometryAdapter = new CategoryAdapter(this);
349 for (FilterRepresentation representation : filtersRepresentations) {
350 mCategoryGeometryAdapter.add(new Action(this, representation));
354 private void processIntent() {
355 Intent intent = getIntent();
356 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
357 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
360 mAction = intent.getAction();
361 mSelectedImageUri = intent.getData();
362 Uri loadUri = mSelectedImageUri;
363 if (mOriginalImageUri != null) {
364 loadUri = mOriginalImageUri;
366 if (loadUri != null) {
367 startLoadBitmap(loadUri);
373 private void setupEditors() {
374 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
375 EditorManager.addEditors(mEditorPlaceHolder);
376 mEditorPlaceHolder.setOldViews(mImageViews);
379 private void fillEditors() {
380 mEditorPlaceHolder.addEditor(new EditorGrad());
381 mEditorPlaceHolder.addEditor(new EditorDraw());
382 mEditorPlaceHolder.addEditor(new BasicEditor());
383 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
384 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
385 mEditorPlaceHolder.addEditor(new EditorRedEye());
386 mEditorPlaceHolder.addEditor(new EditorCrop());
387 mEditorPlaceHolder.addEditor(new EditorFlip());
388 mEditorPlaceHolder.addEditor(new EditorRotate());
389 mEditorPlaceHolder.addEditor(new EditorStraighten());
392 private void setDefaultValues() {
393 Resources res = getResources();
395 // TODO: get those values from XML.
396 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
397 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
398 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
400 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
401 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
402 Spline.setCurveHandle(curveHandle, curveHandleSize);
403 Spline.setCurveWidth((int) getPixelsFromDip(3));
405 ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
406 ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
407 ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
410 private void startLoadBitmap(Uri uri) {
412 final View loading = findViewById(R.id.loading);
413 final View imageShow = findViewById(R.id.imageShow);
414 imageShow.setVisibility(View.INVISIBLE);
415 loading.setVisibility(View.VISIBLE);
416 mShowingTinyPlanet = false;
417 mLoadBitmapTask = new LoadBitmapTask();
418 mLoadBitmapTask.execute(uri);
421 private void fillBorders() {
422 FiltersManager filtersManager = FiltersManager.getManager();
423 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
425 for (int i = 0; i < borders.size(); i++) {
426 FilterRepresentation filter = borders.get(i);
427 filter.setName(getString(R.string.borders));
429 filter.setName(getString(R.string.none));
433 mCategoryBordersAdapter = new CategoryAdapter(this);
434 for (FilterRepresentation representation : borders) {
435 if (representation.getTextId() != 0) {
436 representation.setName(getString(representation.getTextId()));
438 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
442 public CategoryAdapter getCategoryLooksAdapter() {
443 return mCategoryLooksAdapter;
446 public CategoryAdapter getCategoryBordersAdapter() {
447 return mCategoryBordersAdapter;
450 public CategoryAdapter getCategoryGeometryAdapter() {
451 return mCategoryGeometryAdapter;
454 public CategoryAdapter getCategoryFiltersAdapter() {
455 return mCategoryFiltersAdapter;
458 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
459 if (filterRepresentation == null) {
462 ImagePreset oldPreset = MasterImage.getImage().getPreset();
463 ImagePreset copy = new ImagePreset(oldPreset);
464 copy.removeFilter(filterRepresentation);
465 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
466 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
467 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
468 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
472 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
473 if (filterRepresentation == null) {
476 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
479 ImagePreset oldPreset = MasterImage.getImage().getPreset();
480 ImagePreset copy = new ImagePreset(oldPreset);
481 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
482 if (representation == null) {
483 copy.addFilter(filterRepresentation);
485 if (filterRepresentation.allowsSingleInstanceOnly()) {
486 // Don't just update the filter representation. Centralize the
487 // logic in the addFilter(), such that we can keep "None" as
489 copy.removeFilter(representation);
490 copy.addFilter(filterRepresentation);
493 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
494 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
497 public void showRepresentation(FilterRepresentation representation) {
498 if (representation == null) {
502 // TODO: this check is needed because the GeometryMetadata doesn't quite
503 // follow the same pattern as the other filters to update/sync their values.
504 // We thus need to not call useFilterRepresentation() for now, as it
505 // would override the current Geometry. Once GeometryMetadata is fixed,
506 // let's remove the check and call useFilterRepresentation all the time.
507 if (!(representation instanceof GeometryMetadata)) {
508 useFilterRepresentation(representation);
511 // show representation
512 Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
513 loadEditorPanel(representation, mCurrentEditor);
516 public Editor getEditor(int editorID) {
517 return mEditorPlaceHolder.getEditor(editorID);
520 public void setCurrentPanel(int currentPanel) {
521 mCurrentPanel = currentPanel;
524 public int getCurrentPanel() {
525 return mCurrentPanel;
528 public void updateCategories() {
529 ImagePreset preset = mMasterImage.getPreset();
530 mCategoryLooksAdapter.reflectImagePreset(preset);
531 mCategoryBordersAdapter.reflectImagePreset(preset);
534 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
536 protected Boolean doInBackground(Void... params) {
537 MasterImage master = MasterImage.getImage();
538 Rect originalBounds = master.getOriginalBounds();
539 if (master.supportsHighRes()) {
540 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
541 if (highresPreviewSize > originalBounds.width()) {
542 highresPreviewSize = originalBounds.width();
544 Rect bounds = new Rect();
545 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
546 master.getActivity(), highresPreviewSize,
547 master.getOrientation(), bounds);
548 master.setOriginalBounds(bounds);
549 master.setOriginalBitmapHighres(originalHires);
550 mBoundService.setOriginalBitmapHighres(originalHires);
551 master.warnListeners();
557 protected void onPostExecute(Boolean result) {
558 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
559 if (highresBitmap != null) {
560 float highResPreviewScale = (float) highresBitmap.getWidth()
561 / (float) MasterImage.getImage().getOriginalBounds().width();
562 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
567 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
570 public LoadBitmapTask() {
571 mBitmapSize = getScreenImageSize();
575 protected Boolean doInBackground(Uri... params) {
576 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
579 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
584 protected void onProgressUpdate(Boolean... values) {
585 super.onProgressUpdate(values);
590 mShowingTinyPlanet = true;
595 protected void onPostExecute(Boolean result) {
596 MasterImage.setMaster(mMasterImage);
605 if (null == CachingPipeline.getRenderScriptContext()){
606 Log.v(LOGTAG,"RenderScript context destroyed during load");
609 final View loading = findViewById(R.id.loading);
610 loading.setVisibility(View.GONE);
611 final View imageShow = findViewById(R.id.imageShow);
612 imageShow.setVisibility(View.VISIBLE);
614 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
615 mBoundService.setOriginalBitmap(largeBitmap);
617 float previewScale = (float) largeBitmap.getWidth()
618 / (float) MasterImage.getImage().getOriginalBounds().width();
619 mBoundService.setPreviewScaleFactor(previewScale);
620 if (!mShowingTinyPlanet) {
621 mCategoryFiltersAdapter.removeTinyPlanet();
623 MasterImage.getImage().setOriginalGeometry(largeBitmap);
624 mCategoryLooksAdapter.imageLoaded();
625 mCategoryBordersAdapter.imageLoaded();
626 mCategoryGeometryAdapter.imageLoaded();
627 mCategoryFiltersAdapter.imageLoaded();
628 mLoadBitmapTask = null;
630 if (mOriginalPreset != null) {
631 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
632 MasterImage.getImage().setPreset(mOriginalPreset,
633 mOriginalPreset.getLastRepresentation(), true);
634 mOriginalPreset = null;
637 if (mAction == TINY_PLANET_ACTION) {
638 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
641 MasterImage.getImage().notifyGeometryChange();
642 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
643 highresLoad.execute();
644 super.onPostExecute(result);
649 private void clearGalleryBitmapPool() {
650 (new AsyncTask<Void, Void, Void>() {
652 protected Void doInBackground(Void... params) {
653 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
654 GalleryBitmapPool.getInstance().clear();
661 protected void onDestroy() {
662 if (mLoadBitmapTask != null) {
663 mLoadBitmapTask.cancel(false);
669 // TODO: find a more robust way of handling image size selection
670 // for high screen densities.
671 private int getScreenImageSize() {
672 DisplayMetrics outMetrics = new DisplayMetrics();
673 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
674 return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
677 private void showSavingProgress(String albumName) {
678 ProgressDialog progress;
679 if (mSavingProgressDialog != null) {
680 progress = mSavingProgressDialog.get();
681 if (progress != null) {
686 // TODO: Allow cancellation of the saving process
688 if (albumName == null) {
689 progressText = getString(R.string.saving_image);
691 progressText = getString(R.string.filtershow_saving_image, albumName);
693 progress = ProgressDialog.show(this, "", progressText, true, false);
694 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
697 private void hideSavingProgress() {
698 if (mSavingProgressDialog != null) {
699 ProgressDialog progress = mSavingProgressDialog.get();
700 if (progress != null)
705 public void completeSaveImage(Uri saveUri) {
706 if (mSharingImage && mSharedOutputFile != null) {
707 // Image saved, we unblock the content provider
708 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
709 Uri.encode(mSharedOutputFile.getAbsolutePath()));
710 ContentValues values = new ContentValues();
711 values.put(SharedImageProvider.PREPARE, false);
712 getContentResolver().insert(uri, values);
714 setResult(RESULT_OK, new Intent().setData(saveUri));
715 hideSavingProgress();
720 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
721 // First, let's tell the SharedImageProvider that it will need to wait
723 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
724 Uri.encode(mSharedOutputFile.getAbsolutePath()));
725 ContentValues values = new ContentValues();
726 values.put(SharedImageProvider.PREPARE, true);
727 getContentResolver().insert(uri, values);
728 mSharingImage = true;
730 // Process and save the image in the background.
731 showSavingProgress(null);
732 mImageShow.saveImage(this, mSharedOutputFile);
736 private Intent getDefaultShareIntent() {
737 Intent intent = new Intent(Intent.ACTION_SEND);
738 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
739 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
740 intent.setType(SharedImageProvider.MIME_TYPE);
741 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
742 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
743 Uri.encode(mSharedOutputFile.getAbsolutePath()));
744 intent.putExtra(Intent.EXTRA_STREAM, uri);
749 public boolean onCreateOptionsMenu(Menu menu) {
750 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
751 MenuItem showState = menu.findItem(R.id.showImageStateButton);
752 if (mShowingImageStatePanel) {
753 showState.setTitle(R.string.hide_imagestate_panel);
755 showState.setTitle(R.string.show_imagestate_panel);
757 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
758 .getActionProvider();
759 mShareActionProvider.setShareIntent(getDefaultShareIntent());
760 mShareActionProvider.setOnShareTargetSelectedListener(this);
762 MenuItem undoItem = menu.findItem(R.id.undoButton);
763 MenuItem redoItem = menu.findItem(R.id.redoButton);
764 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
765 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
770 public void onPause() {
772 if (mShareActionProvider != null) {
773 mShareActionProvider.setOnShareTargetSelectedListener(null);
778 public void onResume() {
780 if (mShareActionProvider != null) {
781 mShareActionProvider.setOnShareTargetSelectedListener(this);
786 public boolean onOptionsItemSelected(MenuItem item) {
787 switch (item.getItemId()) {
788 case R.id.undoButton: {
789 HistoryManager adapter = mMasterImage.getHistory();
790 int position = adapter.undo();
791 mMasterImage.onHistoryItemClick(position);
794 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
795 UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
798 case R.id.redoButton: {
799 HistoryManager adapter = mMasterImage.getHistory();
800 int position = adapter.redo();
801 mMasterImage.onHistoryItemClick(position);
803 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
804 UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
807 case R.id.resetHistoryButton: {
809 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
810 UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
813 case R.id.showImageStateButton: {
814 toggleImageStatePanel();
815 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
816 UsageStatistics.CATEGORY_BUTTON_PRESS,
817 mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
820 case R.id.exportFlattenButton: {
821 Uri sourceUri = MasterImage.getImage().getUri();
822 File dest = SaveImage.getNewFile(this, sourceUri);
823 Intent processIntent = ProcessingService.getSaveIntent(this, MasterImage.getImage()
824 .getPreset(), dest, getSelectedImageUri(), sourceUri, true);
825 startService(processIntent);
828 case android.R.id.home: {
836 public void enableSave(boolean enable) {
837 if (mSaveButton != null) {
838 mSaveButton.setEnabled(enable);
842 private void fillLooks() {
843 FiltersManager filtersManager = FiltersManager.getManager();
844 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
846 mCategoryLooksAdapter = new CategoryAdapter(this);
847 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
848 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
849 for (FilterRepresentation representation : filtersRepresentations) {
850 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
854 public void setDefaultPreset() {
855 // Default preset (original)
856 ImagePreset preset = new ImagePreset(); // empty
857 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
860 // //////////////////////////////////////////////////////////////////////////////
861 // Some utility functions
862 // TODO: finish the cleanup.
864 public void invalidateViews() {
865 for (ImageShow views : mImageViews) {
871 public void hideImageViews() {
872 for (View view : mImageViews) {
873 view.setVisibility(View.GONE);
875 mEditorPlaceHolder.hide();
878 // //////////////////////////////////////////////////////////////////////////////
879 // imageState panel...
881 public void toggleImageStatePanel() {
882 invalidateOptionsMenu();
883 mShowingImageStatePanel = !mShowingImageStatePanel;
884 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
886 if (panel instanceof EditorPanel) {
887 EditorPanel editorPanel = (EditorPanel) panel;
888 editorPanel.showImageStatePanel(mShowingImageStatePanel);
889 } else if (panel instanceof MainPanel) {
890 MainPanel mainPanel = (MainPanel) panel;
891 mainPanel.showImageStatePanel(mShowingImageStatePanel);
897 public void onConfigurationChanged(Configuration newConfig)
899 super.onConfigurationChanged(newConfig);
905 // mLoadBitmapTask==null implies you have looked at the intent
906 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
907 mCategoryFiltersAdapter.removeTinyPlanet();
909 final View loading = findViewById(R.id.loading);
910 loading.setVisibility(View.GONE);
913 public void setupMasterImage() {
915 HistoryManager historyManager = new HistoryManager();
916 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
918 mMasterImage = MasterImage.getImage();
919 mMasterImage.setHistoryManager(historyManager);
920 mMasterImage.setStateAdapter(imageStateAdapter);
921 mMasterImage.setActivity(this);
923 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
924 mMasterImage.setSupportsHighRes(true);
926 mMasterImage.setSupportsHighRes(false);
930 void resetHistory() {
931 HistoryManager adapter = mMasterImage.getHistory();
933 HistoryItem historyItem = adapter.getItem(0);
934 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
935 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
940 public void showDefaultImageView() {
941 mEditorPlaceHolder.hide();
942 mImageShow.setVisibility(View.VISIBLE);
943 MasterImage.getImage().setCurrentFilter(null);
944 MasterImage.getImage().setCurrentFilterRepresentation(null);
947 public void backToMain() {
948 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
949 if (currentPanel instanceof MainPanel) {
953 showDefaultImageView();
957 public void onBackPressed() {
958 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
959 if (currentPanel instanceof MainPanel) {
960 if (!mImageShow.hasModifications()) {
963 AlertDialog.Builder builder = new AlertDialog.Builder(this);
964 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
965 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
967 public void onClick(DialogInterface dialog, int id) {
971 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
973 public void onClick(DialogInterface dialog, int id) {
984 public void cannotLoadImage() {
985 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
989 // //////////////////////////////////////////////////////////////////////////////
991 public float getPixelsFromDip(float value) {
992 Resources r = getResources();
993 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
994 r.getDisplayMetrics());
998 public void onItemClick(AdapterView<?> parent, View view, int position,
1000 mMasterImage.onHistoryItemClick(position);
1004 public void pickImage() {
1005 Intent intent = new Intent();
1006 intent.setType("image/*");
1007 intent.setAction(Intent.ACTION_GET_CONTENT);
1008 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1013 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1014 if (resultCode == RESULT_OK) {
1015 if (requestCode == SELECT_PICTURE) {
1016 Uri selectedImageUri = data.getData();
1017 startLoadBitmap(selectedImageUri);
1023 public void saveImage() {
1024 if (mImageShow.hasModifications()) {
1025 // Get the name of the album, to which the image will be saved
1026 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1027 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1028 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1029 showSavingProgress(albumName);
1030 mImageShow.saveImage(this, null);
1037 public void done() {
1038 hideSavingProgress();
1039 if (mLoadBitmapTask != null) {
1040 mLoadBitmapTask.cancel(false);
1045 private void extractXMPData() {
1046 XMresults res = XmpPresets.extractXMPData(
1047 getBaseContext(), mMasterImage, getIntent().getData());
1051 mOriginalImageUri = res.originalimage;
1052 mOriginalPreset = res.preset;
1055 public Uri getSelectedImageUri() {
1056 return mSelectedImageUri;