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.pipeline.CachingPipeline;
62 import com.android.gallery3d.filtershow.cache.ImageLoader;
63 import com.android.gallery3d.filtershow.category.Action;
64 import com.android.gallery3d.filtershow.category.CategoryAdapter;
65 import com.android.gallery3d.filtershow.category.MainPanel;
66 import com.android.gallery3d.filtershow.editors.BasicEditor;
67 import com.android.gallery3d.filtershow.editors.Editor;
68 import com.android.gallery3d.filtershow.editors.EditorCrop;
69 import com.android.gallery3d.filtershow.editors.EditorDraw;
70 import com.android.gallery3d.filtershow.editors.EditorFlip;
71 import com.android.gallery3d.filtershow.editors.EditorManager;
72 import com.android.gallery3d.filtershow.editors.EditorPanel;
73 import com.android.gallery3d.filtershow.editors.EditorRedEye;
74 import com.android.gallery3d.filtershow.editors.EditorRotate;
75 import com.android.gallery3d.filtershow.editors.EditorStraighten;
76 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
77 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
78 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
79 import com.android.gallery3d.filtershow.filters.FiltersManager;
80 import com.android.gallery3d.filtershow.filters.ImageFilter;
81 import com.android.gallery3d.filtershow.history.HistoryManager;
82 import com.android.gallery3d.filtershow.history.HistoryItem;
83 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
84 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
85 import com.android.gallery3d.filtershow.imageshow.ImageShow;
86 import com.android.gallery3d.filtershow.imageshow.MasterImage;
87 import com.android.gallery3d.filtershow.imageshow.Spline;
88 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
89 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
90 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
91 import com.android.gallery3d.filtershow.state.StateAdapter;
92 import com.android.gallery3d.filtershow.tools.SaveImage;
93 import com.android.gallery3d.filtershow.tools.XmpPresets;
94 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
95 import com.android.gallery3d.filtershow.ui.FramedTextButton;
96 import com.android.gallery3d.util.GalleryUtils;
97 import com.android.gallery3d.util.UsageStatistics;
98 import com.android.photos.data.GalleryBitmapPool;
101 import java.lang.ref.WeakReference;
102 import java.util.ArrayList;
103 import java.util.Vector;
105 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
106 OnShareTargetSelectedListener {
108 private String mAction = "";
109 MasterImage mMasterImage = null;
111 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
113 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
114 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
115 private ImageShow mImageShow = null;
117 private View mSaveButton = null;
119 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
121 private static final int SELECT_PICTURE = 1;
122 private static final String LOGTAG = "FilterShowActivity";
123 protected static final boolean ANIMATE_PANELS = true;
125 private boolean mShowingTinyPlanet = false;
126 private boolean mShowingImageStatePanel = false;
128 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
130 private ShareActionProvider mShareActionProvider;
131 private File mSharedOutputFile = null;
133 private boolean mSharingImage = false;
135 private WeakReference<ProgressDialog> mSavingProgressDialog;
137 private LoadBitmapTask mLoadBitmapTask;
138 private boolean mLoading = true;
140 private Uri mOriginalImageUri = null;
141 private ImagePreset mOriginalPreset = null;
143 private Uri mSelectedImageUri = null;
145 private CategoryAdapter mCategoryLooksAdapter = null;
146 private CategoryAdapter mCategoryBordersAdapter = null;
147 private CategoryAdapter mCategoryGeometryAdapter = null;
148 private CategoryAdapter mCategoryFiltersAdapter = null;
149 private int mCurrentPanel = MainPanel.LOOKS;
151 private ProcessingService mBoundService;
152 private boolean mIsBound = false;
154 public ProcessingService getProcessingService() {
155 return mBoundService;
158 public boolean isSimpleEditAction() {
159 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
162 private ServiceConnection mConnection = new ServiceConnection() {
163 public void onServiceConnected(ComponentName className, IBinder service) {
165 * This is called when the connection with the service has been
166 * established, giving us the service object we can use to
167 * interact with the service. Because we have bound to a explicit
168 * service that we know is running in our own process, we can
169 * cast its IBinder to a concrete class and directly access it.
171 mBoundService = ((ProcessingService.LocalBinder)service).getService();
172 mBoundService.setFiltershowActivity(FilterShowActivity.this);
173 mBoundService.onStart();
176 public void onServiceDisconnected(ComponentName className) {
178 * This is called when the connection with the service has been
179 * unexpectedly disconnected -- that is, its process crashed.
180 * Because it is running in our same process, we should never
183 mBoundService = null;
187 void doBindService() {
189 * Establish a connection with the service. We use an explicit
190 * class name because we want a specific service implementation that
191 * we know will be running in our own process (and thus won't be
192 * supporting component replacement by other applications).
194 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
195 mConnection, Context.BIND_AUTO_CREATE);
199 void doUnbindService() {
201 // Detach our existing connection.
202 unbindService(mConnection);
207 private void setupPipeline() {
209 ImageFilter.setActivityForMemoryToasts(this);
212 public void updateUIAfterServiceStarted() {
221 public void onCreate(Bundle savedInstanceState) {
222 super.onCreate(savedInstanceState);
224 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
225 if (onlyUsePortrait) {
226 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
228 MasterImage.setMaster(mMasterImage);
230 clearGalleryBitmapPool();
238 UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
239 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
240 UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
243 public boolean isShowingImageStatePanel() {
244 return mShowingImageStatePanel;
247 public void loadMainPanel() {
248 if (findViewById(R.id.main_panel_container) == null) {
251 MainPanel panel = new MainPanel();
252 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
253 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
254 transaction.commit();
257 public void loadEditorPanel(FilterRepresentation representation,
258 final Editor currentEditor) {
259 if (representation.getEditorId() == ImageOnlyEditor.ID) {
260 currentEditor.reflectCurrentFilter();
263 final int currentId = currentEditor.getID();
264 Runnable showEditor = new Runnable() {
267 EditorPanel panel = new EditorPanel();
268 panel.setEditor(currentId);
269 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
270 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
271 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
272 transaction.commit();
275 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
276 boolean doAnimation = false;
277 if (mShowingImageStatePanel
278 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
281 if (doAnimation && main != null && main instanceof MainPanel) {
282 MainPanel mainPanel = (MainPanel) main;
283 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
284 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
285 int panelHeight = container.getHeight() + bottom.getHeight();
286 ViewPropertyAnimator anim = mainPanel.getView().animate();
287 anim.translationY(panelHeight).start();
288 final Handler handler = new Handler();
289 handler.postDelayed(showEditor, anim.getDuration());
295 private void loadXML() {
296 setContentView(R.layout.filtershow_activity);
298 ActionBar actionBar = getActionBar();
299 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
300 actionBar.setCustomView(R.layout.filtershow_actionbar);
302 mSaveButton = actionBar.getCustomView();
303 mSaveButton.setOnClickListener(new OnClickListener() {
305 public void onClick(View view) {
310 mImageShow = (ImageShow) findViewById(R.id.imageShow);
311 mImageViews.add(mImageShow);
315 mEditorPlaceHolder.hide();
316 mImageShow.bindAsImageLoadListener();
321 public void fillCategories() {
328 public void setupStatePanel() {
329 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
332 private void fillEffects() {
333 FiltersManager filtersManager = FiltersManager.getManager();
334 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
335 mCategoryFiltersAdapter = new CategoryAdapter(this);
336 for (FilterRepresentation representation : filtersRepresentations) {
337 if (representation.getTextId() != 0) {
338 representation.setName(getString(representation.getTextId()));
340 mCategoryFiltersAdapter.add(new Action(this, representation));
344 private void fillTools() {
345 FiltersManager filtersManager = FiltersManager.getManager();
346 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
347 mCategoryGeometryAdapter = new CategoryAdapter(this);
348 for (FilterRepresentation representation : filtersRepresentations) {
349 mCategoryGeometryAdapter.add(new Action(this, representation));
353 private void processIntent() {
354 Intent intent = getIntent();
355 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
356 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
359 mAction = intent.getAction();
360 mSelectedImageUri = intent.getData();
361 Uri loadUri = mSelectedImageUri;
362 if (mOriginalImageUri != null) {
363 loadUri = mOriginalImageUri;
365 if (loadUri != null) {
366 startLoadBitmap(loadUri);
372 private void setupEditors() {
373 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
374 EditorManager.addEditors(mEditorPlaceHolder);
375 mEditorPlaceHolder.setOldViews(mImageViews);
378 private void fillEditors() {
379 mEditorPlaceHolder.addEditor(new EditorDraw());
380 mEditorPlaceHolder.addEditor(new BasicEditor());
381 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
382 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
383 mEditorPlaceHolder.addEditor(new EditorRedEye());
384 mEditorPlaceHolder.addEditor(new EditorCrop());
385 mEditorPlaceHolder.addEditor(new EditorFlip());
386 mEditorPlaceHolder.addEditor(new EditorRotate());
387 mEditorPlaceHolder.addEditor(new EditorStraighten());
390 private void setDefaultValues() {
391 Resources res = getResources();
393 // TODO: get those values from XML.
394 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
395 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
396 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
398 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
399 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
400 Spline.setCurveHandle(curveHandle, curveHandleSize);
401 Spline.setCurveWidth((int) getPixelsFromDip(3));
403 ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
404 ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
405 ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
408 private void startLoadBitmap(Uri uri) {
410 final View loading = findViewById(R.id.loading);
411 final View imageShow = findViewById(R.id.imageShow);
412 imageShow.setVisibility(View.INVISIBLE);
413 loading.setVisibility(View.VISIBLE);
414 mShowingTinyPlanet = false;
415 mLoadBitmapTask = new LoadBitmapTask();
416 mLoadBitmapTask.execute(uri);
419 private void fillBorders() {
420 FiltersManager filtersManager = FiltersManager.getManager();
421 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
423 for (int i = 0; i < borders.size(); i++) {
424 FilterRepresentation filter = borders.get(i);
425 filter.setName(getString(R.string.borders));
427 filter.setName(getString(R.string.none));
431 mCategoryBordersAdapter = new CategoryAdapter(this);
432 for (FilterRepresentation representation : borders) {
433 if (representation.getTextId() != 0) {
434 representation.setName(getString(representation.getTextId()));
436 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
440 public CategoryAdapter getCategoryLooksAdapter() {
441 return mCategoryLooksAdapter;
444 public CategoryAdapter getCategoryBordersAdapter() {
445 return mCategoryBordersAdapter;
448 public CategoryAdapter getCategoryGeometryAdapter() {
449 return mCategoryGeometryAdapter;
452 public CategoryAdapter getCategoryFiltersAdapter() {
453 return mCategoryFiltersAdapter;
456 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
457 if (filterRepresentation == null) {
460 ImagePreset oldPreset = MasterImage.getImage().getPreset();
461 ImagePreset copy = new ImagePreset(oldPreset);
462 copy.removeFilter(filterRepresentation);
463 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
464 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
465 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
466 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
470 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
471 if (filterRepresentation == null) {
474 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
477 ImagePreset oldPreset = MasterImage.getImage().getPreset();
478 ImagePreset copy = new ImagePreset(oldPreset);
479 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
480 if (representation == null) {
481 copy.addFilter(filterRepresentation);
483 if (filterRepresentation.allowsSingleInstanceOnly()) {
484 // Don't just update the filter representation. Centralize the
485 // logic in the addFilter(), such that we can keep "None" as
487 copy.removeFilter(representation);
488 copy.addFilter(filterRepresentation);
491 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
492 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
495 public void showRepresentation(FilterRepresentation representation) {
496 if (representation == null) {
500 // TODO: this check is needed because the GeometryMetadata doesn't quite
501 // follow the same pattern as the other filters to update/sync their values.
502 // We thus need to not call useFilterRepresentation() for now, as it
503 // would override the current Geometry. Once GeometryMetadata is fixed,
504 // let's remove the check and call useFilterRepresentation all the time.
505 if (!(representation instanceof GeometryMetadata)) {
506 useFilterRepresentation(representation);
509 // show representation
510 Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
511 loadEditorPanel(representation, mCurrentEditor);
514 public Editor getEditor(int editorID) {
515 return mEditorPlaceHolder.getEditor(editorID);
518 public void setCurrentPanel(int currentPanel) {
519 mCurrentPanel = currentPanel;
522 public int getCurrentPanel() {
523 return mCurrentPanel;
526 public void updateCategories() {
527 ImagePreset preset = mMasterImage.getPreset();
528 mCategoryLooksAdapter.reflectImagePreset(preset);
529 mCategoryBordersAdapter.reflectImagePreset(preset);
532 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
534 protected Boolean doInBackground(Void... params) {
535 MasterImage master = MasterImage.getImage();
536 Rect originalBounds = master.getOriginalBounds();
537 if (master.supportsHighRes()) {
538 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
539 if (highresPreviewSize > originalBounds.width()) {
540 highresPreviewSize = originalBounds.width();
542 Rect bounds = new Rect();
543 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
544 master.getActivity(), highresPreviewSize,
545 master.getOrientation(), bounds);
546 master.setOriginalBounds(bounds);
547 master.setOriginalBitmapHighres(originalHires);
548 mBoundService.setOriginalBitmapHighres(originalHires);
549 master.warnListeners();
555 protected void onPostExecute(Boolean result) {
556 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
557 if (highresBitmap != null) {
558 float highResPreviewScale = (float) highresBitmap.getWidth()
559 / (float) MasterImage.getImage().getOriginalBounds().width();
560 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
565 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
568 public LoadBitmapTask() {
569 mBitmapSize = getScreenImageSize();
573 protected Boolean doInBackground(Uri... params) {
574 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
577 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
582 protected void onProgressUpdate(Boolean... values) {
583 super.onProgressUpdate(values);
588 mShowingTinyPlanet = true;
593 protected void onPostExecute(Boolean result) {
594 MasterImage.setMaster(mMasterImage);
603 if (null == CachingPipeline.getRenderScriptContext()){
604 Log.v(LOGTAG,"RenderScript context destroyed during load");
607 final View loading = findViewById(R.id.loading);
608 loading.setVisibility(View.GONE);
609 final View imageShow = findViewById(R.id.imageShow);
610 imageShow.setVisibility(View.VISIBLE);
612 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
613 mBoundService.setOriginalBitmap(largeBitmap);
615 float previewScale = (float) largeBitmap.getWidth()
616 / (float) MasterImage.getImage().getOriginalBounds().width();
617 mBoundService.setPreviewScaleFactor(previewScale);
618 if (!mShowingTinyPlanet) {
619 mCategoryFiltersAdapter.removeTinyPlanet();
621 MasterImage.getImage().setOriginalGeometry(largeBitmap);
622 mCategoryLooksAdapter.imageLoaded();
623 mCategoryBordersAdapter.imageLoaded();
624 mCategoryGeometryAdapter.imageLoaded();
625 mCategoryFiltersAdapter.imageLoaded();
626 mLoadBitmapTask = null;
628 if (mOriginalPreset != null) {
629 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
630 MasterImage.getImage().setPreset(mOriginalPreset,
631 mOriginalPreset.getLastRepresentation(), true);
632 mOriginalPreset = null;
635 if (mAction == TINY_PLANET_ACTION) {
636 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
639 MasterImage.getImage().notifyGeometryChange();
640 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
641 highresLoad.execute();
642 super.onPostExecute(result);
647 private void clearGalleryBitmapPool() {
648 (new AsyncTask<Void, Void, Void>() {
650 protected Void doInBackground(Void... params) {
651 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
652 GalleryBitmapPool.getInstance().clear();
659 protected void onDestroy() {
660 if (mLoadBitmapTask != null) {
661 mLoadBitmapTask.cancel(false);
667 // TODO: find a more robust way of handling image size selection
668 // for high screen densities.
669 private int getScreenImageSize() {
670 DisplayMetrics outMetrics = new DisplayMetrics();
671 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
672 return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
675 private void showSavingProgress(String albumName) {
676 ProgressDialog progress;
677 if (mSavingProgressDialog != null) {
678 progress = mSavingProgressDialog.get();
679 if (progress != null) {
684 // TODO: Allow cancellation of the saving process
686 if (albumName == null) {
687 progressText = getString(R.string.saving_image);
689 progressText = getString(R.string.filtershow_saving_image, albumName);
691 progress = ProgressDialog.show(this, "", progressText, true, false);
692 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
695 private void hideSavingProgress() {
696 if (mSavingProgressDialog != null) {
697 ProgressDialog progress = mSavingProgressDialog.get();
698 if (progress != null)
703 public void completeSaveImage(Uri saveUri) {
704 if (mSharingImage && mSharedOutputFile != null) {
705 // Image saved, we unblock the content provider
706 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
707 Uri.encode(mSharedOutputFile.getAbsolutePath()));
708 ContentValues values = new ContentValues();
709 values.put(SharedImageProvider.PREPARE, false);
710 getContentResolver().insert(uri, values);
712 setResult(RESULT_OK, new Intent().setData(saveUri));
713 hideSavingProgress();
718 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
719 // First, let's tell the SharedImageProvider that it will need to wait
721 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
722 Uri.encode(mSharedOutputFile.getAbsolutePath()));
723 ContentValues values = new ContentValues();
724 values.put(SharedImageProvider.PREPARE, true);
725 getContentResolver().insert(uri, values);
726 mSharingImage = true;
728 // Process and save the image in the background.
729 showSavingProgress(null);
730 mImageShow.saveImage(this, mSharedOutputFile);
734 private Intent getDefaultShareIntent() {
735 Intent intent = new Intent(Intent.ACTION_SEND);
736 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
737 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
738 intent.setType(SharedImageProvider.MIME_TYPE);
739 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
740 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
741 Uri.encode(mSharedOutputFile.getAbsolutePath()));
742 intent.putExtra(Intent.EXTRA_STREAM, uri);
747 public boolean onCreateOptionsMenu(Menu menu) {
748 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
749 MenuItem showState = menu.findItem(R.id.showImageStateButton);
750 if (mShowingImageStatePanel) {
751 showState.setTitle(R.string.hide_imagestate_panel);
753 showState.setTitle(R.string.show_imagestate_panel);
755 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
756 .getActionProvider();
757 mShareActionProvider.setShareIntent(getDefaultShareIntent());
758 mShareActionProvider.setOnShareTargetSelectedListener(this);
760 MenuItem undoItem = menu.findItem(R.id.undoButton);
761 MenuItem redoItem = menu.findItem(R.id.redoButton);
762 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
763 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
768 public void onPause() {
770 if (mShareActionProvider != null) {
771 mShareActionProvider.setOnShareTargetSelectedListener(null);
776 public void onResume() {
778 if (mShareActionProvider != null) {
779 mShareActionProvider.setOnShareTargetSelectedListener(this);
784 public boolean onOptionsItemSelected(MenuItem item) {
785 switch (item.getItemId()) {
786 case R.id.undoButton: {
787 HistoryManager adapter = mMasterImage.getHistory();
788 int position = adapter.undo();
789 mMasterImage.onHistoryItemClick(position);
792 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
793 UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
796 case R.id.redoButton: {
797 HistoryManager adapter = mMasterImage.getHistory();
798 int position = adapter.redo();
799 mMasterImage.onHistoryItemClick(position);
801 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
802 UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
805 case R.id.resetHistoryButton: {
807 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
808 UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
811 case R.id.showImageStateButton: {
812 toggleImageStatePanel();
813 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
814 UsageStatistics.CATEGORY_BUTTON_PRESS,
815 mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
818 case R.id.exportFlattenButton: {
819 Uri sourceUri = MasterImage.getImage().getUri();
820 File dest = SaveImage.getNewFile(this, sourceUri);
821 Intent processIntent = ProcessingService.getSaveIntent(this, MasterImage.getImage()
822 .getPreset(), dest, getSelectedImageUri(), sourceUri, true);
823 startService(processIntent);
826 case android.R.id.home: {
834 public void enableSave(boolean enable) {
835 if (mSaveButton != null) {
836 mSaveButton.setEnabled(enable);
840 private void fillLooks() {
841 FiltersManager filtersManager = FiltersManager.getManager();
842 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
844 mCategoryLooksAdapter = new CategoryAdapter(this);
845 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
846 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
847 for (FilterRepresentation representation : filtersRepresentations) {
848 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
852 public void setDefaultPreset() {
853 // Default preset (original)
854 ImagePreset preset = new ImagePreset(); // empty
855 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
858 // //////////////////////////////////////////////////////////////////////////////
859 // Some utility functions
860 // TODO: finish the cleanup.
862 public void invalidateViews() {
863 for (ImageShow views : mImageViews) {
869 public void hideImageViews() {
870 for (View view : mImageViews) {
871 view.setVisibility(View.GONE);
873 mEditorPlaceHolder.hide();
876 // //////////////////////////////////////////////////////////////////////////////
877 // imageState panel...
879 public void toggleImageStatePanel() {
880 invalidateOptionsMenu();
881 mShowingImageStatePanel = !mShowingImageStatePanel;
882 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
884 if (panel instanceof EditorPanel) {
885 EditorPanel editorPanel = (EditorPanel) panel;
886 editorPanel.showImageStatePanel(mShowingImageStatePanel);
887 } else if (panel instanceof MainPanel) {
888 MainPanel mainPanel = (MainPanel) panel;
889 mainPanel.showImageStatePanel(mShowingImageStatePanel);
895 public void onConfigurationChanged(Configuration newConfig)
897 super.onConfigurationChanged(newConfig);
903 // mLoadBitmapTask==null implies you have looked at the intent
904 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
905 mCategoryFiltersAdapter.removeTinyPlanet();
907 final View loading = findViewById(R.id.loading);
908 loading.setVisibility(View.GONE);
911 public void setupMasterImage() {
913 HistoryManager historyManager = new HistoryManager();
914 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
916 mMasterImage = MasterImage.getImage();
917 mMasterImage.setHistoryManager(historyManager);
918 mMasterImage.setStateAdapter(imageStateAdapter);
919 mMasterImage.setActivity(this);
921 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
922 mMasterImage.setSupportsHighRes(true);
924 mMasterImage.setSupportsHighRes(false);
928 void resetHistory() {
929 HistoryManager adapter = mMasterImage.getHistory();
931 HistoryItem historyItem = adapter.getItem(0);
932 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
933 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
938 public void showDefaultImageView() {
939 mEditorPlaceHolder.hide();
940 mImageShow.setVisibility(View.VISIBLE);
941 MasterImage.getImage().setCurrentFilter(null);
942 MasterImage.getImage().setCurrentFilterRepresentation(null);
945 public void backToMain() {
946 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
947 if (currentPanel instanceof MainPanel) {
951 showDefaultImageView();
955 public void onBackPressed() {
956 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
957 if (currentPanel instanceof MainPanel) {
958 if (!mImageShow.hasModifications()) {
961 AlertDialog.Builder builder = new AlertDialog.Builder(this);
962 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
963 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
965 public void onClick(DialogInterface dialog, int id) {
969 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
971 public void onClick(DialogInterface dialog, int id) {
982 public void cannotLoadImage() {
983 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
987 // //////////////////////////////////////////////////////////////////////////////
989 public float getPixelsFromDip(float value) {
990 Resources r = getResources();
991 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
992 r.getDisplayMetrics());
996 public void onItemClick(AdapterView<?> parent, View view, int position,
998 mMasterImage.onHistoryItemClick(position);
1002 public void pickImage() {
1003 Intent intent = new Intent();
1004 intent.setType("image/*");
1005 intent.setAction(Intent.ACTION_GET_CONTENT);
1006 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1011 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1012 if (resultCode == RESULT_OK) {
1013 if (requestCode == SELECT_PICTURE) {
1014 Uri selectedImageUri = data.getData();
1015 startLoadBitmap(selectedImageUri);
1021 public void saveImage() {
1022 if (mImageShow.hasModifications()) {
1023 // Get the name of the album, to which the image will be saved
1024 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1025 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1026 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1027 showSavingProgress(albumName);
1028 mImageShow.saveImage(this, null);
1035 public void done() {
1036 hideSavingProgress();
1037 if (mLoadBitmapTask != null) {
1038 mLoadBitmapTask.cancel(false);
1043 private void extractXMPData() {
1044 XMresults res = XmpPresets.extractXMPData(
1045 getBaseContext(), mMasterImage, getIntent().getData());
1049 mOriginalImageUri = res.originalimage;
1050 mOriginalPreset = res.preset;
1053 public Uri getSelectedImageUri() {
1054 return mSelectedImageUri;