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.Activity;
21 import android.app.ProgressDialog;
22 import android.app.WallpaperManager;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.res.Configuration;
27 import android.content.res.Resources;
28 import android.graphics.Bitmap;
29 import android.graphics.BitmapFactory;
30 import android.graphics.Color;
31 import android.graphics.Point;
32 import android.graphics.drawable.Drawable;
33 import android.net.Uri;
34 import android.os.AsyncTask;
35 import android.os.Bundle;
36 import android.provider.MediaStore;
37 import android.util.DisplayMetrics;
38 import android.util.Log;
39 import android.util.TypedValue;
40 import android.view.*;
41 import android.view.View.OnClickListener;
42 import android.widget.AdapterView;
43 import android.widget.AdapterView.OnItemClickListener;
44 import android.widget.FrameLayout;
45 import android.widget.ImageButton;
46 import android.widget.LinearLayout;
47 import android.widget.ListView;
48 import android.widget.ShareActionProvider;
49 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
50 import android.widget.Toast;
52 import com.android.gallery3d.R;
53 import com.android.gallery3d.data.LocalAlbum;
54 import com.android.gallery3d.filtershow.cache.FilteringPipeline;
55 import com.android.gallery3d.filtershow.cache.ImageLoader;
56 import com.android.gallery3d.filtershow.editors.BasicEditor;
57 import com.android.gallery3d.filtershow.editors.EditorCrop;
58 import com.android.gallery3d.filtershow.editors.EditorDraw;
59 import com.android.gallery3d.filtershow.editors.EditorFlip;
60 import com.android.gallery3d.filtershow.editors.EditorInfo;
61 import com.android.gallery3d.filtershow.editors.EditorManager;
62 import com.android.gallery3d.filtershow.editors.EditorRedEye;
63 import com.android.gallery3d.filtershow.editors.EditorRotate;
64 import com.android.gallery3d.filtershow.editors.EditorStraighten;
65 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
66 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
67 import com.android.gallery3d.filtershow.filters.*;
68 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
69 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
70 import com.android.gallery3d.filtershow.imageshow.ImageShow;
71 import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet;
72 import com.android.gallery3d.filtershow.imageshow.ImageZoom;
73 import com.android.gallery3d.filtershow.imageshow.MasterImage;
74 import com.android.gallery3d.filtershow.presets.ImagePreset;
75 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
76 import com.android.gallery3d.filtershow.tools.BitmapTask;
77 import com.android.gallery3d.filtershow.tools.SaveCopyTask;
78 import com.android.gallery3d.filtershow.ui.FilterIconButton;
79 import com.android.gallery3d.filtershow.ui.FramedTextButton;
80 import com.android.gallery3d.filtershow.ui.Spline;
81 import com.android.gallery3d.util.GalleryUtils;
82 import com.android.photos.data.GalleryBitmapPool;
85 import java.io.IOException;
86 import java.lang.ref.WeakReference;
87 import java.util.Vector;
89 public class FilterShowActivity extends Activity implements OnItemClickListener,
90 OnShareTargetSelectedListener {
92 // fields for supporting crop action
93 public static final String CROP_ACTION = "com.android.camera.action.CROP";
94 private CropExtras mCropExtras = null;
95 private String mAction = "";
96 MasterImage mMasterImage = null;
98 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
99 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
100 public static final int MAX_BMAP_IN_INTENT = 990000;
101 private final PanelController mPanelController = new PanelController();
102 private ImageLoader mImageLoader = null;
103 private ImageShow mImageShow = null;
104 private ImageTinyPlanet mImageTinyPlanet = null;
106 private View mSaveButton = null;
108 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
110 private static final int SELECT_PICTURE = 1;
111 private static final String LOGTAG = "FilterShowActivity";
112 protected static final boolean ANIMATE_PANELS = true;
113 private static int mImageBorderSize = 4; // in percent
115 private boolean mShowingTinyPlanet = false;
116 private boolean mShowingHistoryPanel = false;
117 private boolean mShowingImageStatePanel = false;
119 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
121 private ShareActionProvider mShareActionProvider;
122 private File mSharedOutputFile = null;
124 private boolean mSharingImage = false;
126 private WeakReference<ProgressDialog> mSavingProgressDialog;
128 private LoadBitmapTask mLoadBitmapTask;
129 private FilterIconButton mNullFxFilter;
130 private FilterIconButton mNullBorderFilter;
131 private int mIconSeedSize = 140;
135 public void onCreate(Bundle savedInstanceState) {
136 super.onCreate(savedInstanceState);
138 clearGalleryBitmapPool();
141 ImageFilterRS.createRenderscriptContext(this);
146 if (getResources().getConfiguration().orientation
147 == Configuration.ORIENTATION_LANDSCAPE) {
148 mShowingImageStatePanel = true;
150 if (mShowingHistoryPanel) {
151 findViewById(R.id.historyPanel).setVisibility(View.VISIBLE);
153 findViewById(R.id.historyPanel).setVisibility(View.GONE);
155 if (mShowingImageStatePanel) {
156 findViewById(R.id.imageStatePanel).setVisibility(View.VISIBLE);
158 findViewById(R.id.imageStatePanel).setVisibility(View.GONE);
166 private void loadXML() {
167 setContentView(R.layout.filtershow_activity);
169 ((ViewStub) findViewById(R.id.stateCategoryStub)).inflate();
170 ((ViewStub) findViewById(R.id.editorPanelStub)).inflate();
171 ((ViewStub) findViewById(R.id.historyPanelStub)).inflate();
172 ((ViewStub) findViewById(R.id.statePanelStub)).inflate();
174 ActionBar actionBar = getActionBar();
175 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
176 actionBar.setCustomView(R.layout.filtershow_actionbar);
178 mSaveButton = actionBar.getCustomView();
179 mSaveButton.setOnClickListener(new OnClickListener() {
181 public void onClick(View view) {
186 mImageShow = (ImageShow) findViewById(R.id.imageShow);
187 mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet);
188 mImageViews.add(mImageShow);
189 mImageViews.add(mImageTinyPlanet);
193 mEditorPlaceHolder.hide();
195 mImageShow.setImageLoader(mImageLoader);
196 mImageTinyPlanet.setImageLoader(mImageLoader);
198 mPanelController.clear();
199 mPanelController.setActivity(this);
200 mPanelController.setEditorPlaceHolder(mEditorPlaceHolder);
202 mPanelController.addImageView(findViewById(R.id.imageShow));
203 mPanelController.addImageView(findViewById(R.id.imageTinyPlanet));
205 mPanelController.addPanel(R.id.fxButton, R.id.fxList, 0);
206 mPanelController.addPanel(R.id.borderButton, R.id.bordersList, 1);
207 mPanelController.addPanel(R.id.geometryButton, R.id.geometryList, 2);
208 mPanelController.addPanel(R.id.colorsButton, R.id.colorsFxList, 3);
210 fillFx((LinearLayout) findViewById(R.id.listFilters), R.id.fxButton);
211 LoadBordersTask loadBorders = new LoadBordersTask((LinearLayout) findViewById(R.id.listBorders));
212 loadBorders.execute();
216 mPanelController.addView(findViewById(R.id.applyEffect));
222 public void setupHistoryPanel() {
223 findViewById(R.id.resetOperationsButton).setOnClickListener(
224 createOnClickResetOperationsButton());
225 ListView operationsList = (ListView) findViewById(R.id.operationsList);
226 operationsList.setAdapter(mMasterImage.getHistory());
227 operationsList.setOnItemClickListener(this);
230 public void setupStatePanel() {
231 ListView imageStateList = (ListView) findViewById(R.id.imageStateList);
232 imageStateList.setAdapter(mMasterImage.getState());
233 mImageLoader.setAdapter(mMasterImage.getHistory());
234 mPanelController.setRowPanel(findViewById(R.id.secondRowPanel));
235 mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList));
236 mPanelController.setCurrentPanel(R.id.fxButton);
239 private void fillPanel(Vector<FilterRepresentation> representations, int layoutId, int buttonId) {
240 ImageButton button = (ImageButton) findViewById(buttonId);
241 LinearLayout layout = (LinearLayout) findViewById(layoutId);
243 for (FilterRepresentation representation : representations) {
244 setupFilterRepresentationButton(representation, layout, button);
248 private void fillFilters() {
249 Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
250 FiltersManager filtersManager = FiltersManager.getManager();
251 filtersManager.addEffects(filtersRepresentations);
252 fillPanel(filtersRepresentations, R.id.listColorsFx, R.id.colorsButton);
255 private void fillGeometry() {
256 Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
257 FiltersManager filtersManager = FiltersManager.getManager();
259 GeometryMetadata geo = new GeometryMetadata();
260 int[] editorsId = geo.getEditorIds();
261 for (int i = 0; i < editorsId.length; i++) {
262 int editorId = editorsId[i];
263 GeometryMetadata geometry = new GeometryMetadata(geo);
264 geometry.setEditorId(editorId);
265 EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId);
266 geometry.setTextId(editorInfo.getTextId());
267 geometry.setOverlayId(editorInfo.getOverlayId());
268 geometry.setOverlayOnly(editorInfo.getOverlayOnly());
269 filtersRepresentations.add(geometry);
272 filtersManager.addTools(filtersRepresentations);
273 fillPanel(filtersRepresentations, R.id.listGeometry, R.id.geometryButton);
276 private void processIntent() {
277 Intent intent = getIntent();
278 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
279 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
282 mAction = intent.getAction();
284 if (intent.getData() != null) {
285 startLoadBitmap(intent.getData());
290 // Handle behavior for various actions
291 if (mAction.equalsIgnoreCase(CROP_ACTION)) {
292 Bundle extras = intent.getExtras();
293 if (extras != null) {
294 mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
295 extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
296 extras.getBoolean(CropExtras.KEY_SCALE, true) &&
297 extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
298 extras.getInt(CropExtras.KEY_ASPECT_X, 0),
299 extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
300 extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
301 extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
302 (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
303 extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
304 extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
305 extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
306 extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
308 if (mCropExtras.getShowWhenLocked()) {
309 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
311 mImageShow.getImagePreset().mGeoData.setCropExtras(mCropExtras);
313 // FIXME: moving to editors breaks the crop action
314 EditorCrop crop = (EditorCrop) mEditorPlaceHolder.getEditor(EditorCrop.ID);
316 crop.setExtras(mCropExtras);
317 String s = getString(R.string.Fixed);
318 crop.setAspectString(s);
319 crop.setCropActionFlag(true);
320 mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0
321 && mCropExtras.getAspectY() > 0);
326 private void setupEditors() {
327 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
328 EditorManager.addEditors(mEditorPlaceHolder);
329 mEditorPlaceHolder.setOldViews(mImageViews);
330 mEditorPlaceHolder.setImageLoader(mImageLoader);
333 private void fillEditors() {
334 mEditorPlaceHolder.addEditor(new EditorDraw());
335 mEditorPlaceHolder.addEditor(new BasicEditor());
336 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
337 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
338 mEditorPlaceHolder.addEditor(new EditorRedEye());
339 mEditorPlaceHolder.addEditor(new EditorCrop());
340 mEditorPlaceHolder.addEditor(new EditorFlip());
341 mEditorPlaceHolder.addEditor(new EditorRotate());
342 mEditorPlaceHolder.addEditor(new EditorStraighten());
345 private void setDefaultValues() {
346 ImageFilter.setActivityForMemoryToasts(this);
348 Resources res = getResources();
349 // TODO: add a mechanism to set the resources in FiltersManagmer
350 ImageFilterBorder filterBorder = (ImageFilterBorder) FiltersManager.getManager().getFilter(ImageFilterBorder.class);
351 filterBorder.setResources(res);
352 filterBorder = (ImageFilterBorder) FiltersManager.getPreviewManager().getFilter(ImageFilterBorder.class);
353 filterBorder.setResources(res);
354 ImageFilterFx filterFx = (ImageFilterFx) FiltersManager.getManager().getFilter(ImageFilterFx.class);
355 filterFx.setResources(res);
356 filterFx = (ImageFilterFx) FiltersManager.getPreviewManager().getFilter(ImageFilterFx.class);
357 filterFx.setResources(res);
359 ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen));
360 // TODO: get those values from XML.
361 ImageZoom.setZoomedSize(getPixelsFromDip(256));
362 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
363 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
364 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
365 ImageShow.setTextSize((int) getPixelsFromDip(12));
366 ImageShow.setTextPadding((int) getPixelsFromDip(10));
367 ImageShow.setOriginalTextMargin((int) getPixelsFromDip(4));
368 ImageShow.setOriginalTextSize((int) getPixelsFromDip(18));
369 ImageShow.setOriginalText(res.getString(R.string.original_picture_text));
370 mIconSeedSize = res.getDimensionPixelSize(R.dimen.thumbnail_size);
371 // TODO: pick correct value
372 // MasterImage.setIconSeedSize(mIconSeedSize);
374 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
375 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
376 Spline.setCurveHandle(curveHandle, curveHandleSize);
377 Spline.setCurveWidth((int) getPixelsFromDip(3));
379 ImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
380 ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
381 ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
384 private void startLoadBitmap(Uri uri) {
385 final View filters = findViewById(R.id.filtersPanel);
386 final View loading = findViewById(R.id.loading);
387 final View imageShow = findViewById(R.id.imageShow);
388 imageShow.setVisibility(View.INVISIBLE);
389 filters.setVisibility(View.INVISIBLE);
390 loading.setVisibility(View.VISIBLE);
392 View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
393 if (tinyPlanetView != null) {
394 mShowingTinyPlanet = false;
395 tinyPlanetView.setVisibility(View.GONE);
397 mLoadBitmapTask = new LoadBitmapTask(tinyPlanetView);
398 mLoadBitmapTask.execute(uri);
401 private class LoadBordersTask extends AsyncTask<Void, Boolean, Boolean> {
402 Vector<FilterRepresentation> mBorders;
405 public LoadBordersTask(LinearLayout list) {
407 mBorders = new Vector<FilterRepresentation>();
411 protected Boolean doInBackground(Void... params) {
412 mBorders.add(new FilterImageBorderRepresentation(0));
413 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5));
414 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush));
415 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge));
416 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e));
417 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape));
418 mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0));
419 mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, mImageBorderSize));
420 mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0));
421 mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, mImageBorderSize));
422 int creamColor = Color.argb(255, 237, 237, 227);
423 mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0));
424 mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, mImageBorderSize));
429 protected void onPostExecute(Boolean result) {
433 for (int i = 0; i < mBorders.size(); i++) {
434 FilterRepresentation filter = mBorders.elementAt(i);
435 filter.setName(getString(R.string.borders));
437 filter.setName(getString(R.string.none));
439 ImageButton borderButton = (ImageButton) findViewById(R.id.borderButton);
440 FilterIconButton b = setupFilterRepresentationButton(filter, mList, borderButton);
442 mNullBorderFilter = b;
443 mNullBorderFilter.setSelected(true);
450 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
451 View mTinyPlanetButton;
454 public LoadBitmapTask(View button) {
455 mTinyPlanetButton = button;
456 mBitmapSize = getScreenImageSize();
460 protected Boolean doInBackground(Uri... params) {
461 if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
464 publishProgress(mImageLoader.queryLightCycle360());
469 protected void onProgressUpdate(Boolean... values) {
470 super.onProgressUpdate(values);
475 mShowingTinyPlanet = true;
476 mTinyPlanetButton.setVisibility(View.VISIBLE);
481 protected void onPostExecute(Boolean result) {
491 final View loading = findViewById(R.id.loading);
492 loading.setVisibility(View.GONE);
493 final View filters = findViewById(R.id.filtersPanel);
494 filters.setVisibility(View.VISIBLE);
495 if (PanelController.useAnimations()) {
496 float y = filters.getY();
497 filters.setY(y + filters.getHeight());
498 filters.animate().setDuration(600).y(y).withLayer().start();
500 final View imageShow = findViewById(R.id.imageShow);
501 imageShow.setVisibility(View.VISIBLE);
503 Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge();
504 FilteringPipeline pipeline = FilteringPipeline.getPipeline();
505 pipeline.setOriginal(largeBitmap);
506 float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width();
507 pipeline.setPreviewScaleFactor(previewScale);
510 MasterImage.getImage().setOriginalGeometry(largeBitmap);
511 mLoadBitmapTask = null;
513 if (mAction == CROP_ACTION) {
514 mPanelController.showComponent(findViewById(EditorCrop.ID));
515 } else if (mAction == TINY_PLANET_ACTION) {
516 mPanelController.showComponent(findViewById(EditorTinyPlanet.ID));
519 super.onPostExecute(result);
524 private void clearGalleryBitmapPool() {
525 (new AsyncTask<Void, Void, Void>() {
527 protected Void doInBackground(Void... params) {
528 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
529 GalleryBitmapPool.getInstance().clear();
535 private void fillButtonIcons() {
536 Bitmap bmap = mImageLoader.getOriginalBitmapSmall();
537 if (bmap != null && bmap.getWidth() > 0 && bmap.getHeight() > 0) {
538 float w = bmap.getWidth();
539 float h = bmap.getHeight();
540 float f = mIconSeedSize / Math.min(w, h);
543 bmap = Bitmap.createScaledBitmap(bmap, (int) w, (int) h, true);
545 LinearLayout listColors = (LinearLayout) findViewById(R.id.listColorsFx);
546 int num_colors_buttons = listColors.getChildCount();
547 for (int i = 0; i < num_colors_buttons; i++) {
548 FilterIconButton b = (FilterIconButton) listColors.getChildAt(i);
552 LinearLayout listFilters = (LinearLayout) findViewById(R.id.listFilters);
553 int num_filters_buttons = listFilters.getChildCount();
554 for (int i = 0; i < num_filters_buttons; i++) {
555 FilterIconButton b = (FilterIconButton) listFilters.getChildAt(i);
559 LinearLayout listBorders = (LinearLayout) findViewById(R.id.listBorders);
560 int num_borders_buttons = listBorders.getChildCount();
561 for (int i = 0; i < num_borders_buttons; i++) {
562 FilterIconButton b = (FilterIconButton) listBorders.getChildAt(i);
570 protected void onDestroy() {
571 if (mLoadBitmapTask != null) {
572 mLoadBitmapTask.cancel(false);
574 // TODO: Using singletons is a bad design choice for many of these
575 // due static reference leaks and in general. Please refactor.
577 ImageFilterRS.destroyRenderScriptContext();
578 FilteringPipeline.reset();
579 ImageFilter.resetStatics();
580 FiltersManager.reset();
584 private int translateMainPanel(View viewPanel) {
585 int accessoryPanelWidth = viewPanel.getWidth();
586 int mainViewWidth = findViewById(R.id.mainView).getWidth();
587 int mainPanelWidth = mImageShow.getDisplayedImageBounds().width();
588 if (mainPanelWidth == 0) {
589 mainPanelWidth = mainViewWidth;
591 int filtersPanelWidth = findViewById(R.id.filtersPanel).getWidth();
592 if (mainPanelWidth < filtersPanelWidth) {
593 mainPanelWidth = filtersPanelWidth;
595 int leftOver = mainViewWidth - mainPanelWidth - accessoryPanelWidth;
597 return -accessoryPanelWidth;
602 private int getScreenImageSize() {
603 DisplayMetrics metrics = new DisplayMetrics();
604 Display display = getWindowManager().getDefaultDisplay();
605 Point size = new Point();
606 display.getSize(size);
607 display.getMetrics(metrics);
608 int msize = Math.min(size.x, size.y);
609 return (133 * msize) / metrics.densityDpi;
612 private void showSavingProgress(String albumName) {
613 ProgressDialog progress;
614 if (mSavingProgressDialog != null) {
615 progress = mSavingProgressDialog.get();
616 if (progress != null) {
621 // TODO: Allow cancellation of the saving process
623 if (albumName == null) {
624 progressText = getString(R.string.saving_image);
626 progressText = getString(R.string.filtershow_saving_image, albumName);
628 progress = ProgressDialog.show(this, "", progressText, true, false);
629 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
632 private void hideSavingProgress() {
633 if (mSavingProgressDialog != null) {
634 ProgressDialog progress = mSavingProgressDialog.get();
635 if (progress != null)
640 public void completeSaveImage(Uri saveUri) {
641 if (mSharingImage && mSharedOutputFile != null) {
642 // Image saved, we unblock the content provider
643 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
644 Uri.encode(mSharedOutputFile.getAbsolutePath()));
645 ContentValues values = new ContentValues();
646 values.put(SharedImageProvider.PREPARE, false);
647 getContentResolver().insert(uri, values);
649 setResult(RESULT_OK, new Intent().setData(saveUri));
650 hideSavingProgress();
655 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
656 // First, let's tell the SharedImageProvider that it will need to wait
658 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
659 Uri.encode(mSharedOutputFile.getAbsolutePath()));
660 ContentValues values = new ContentValues();
661 values.put(SharedImageProvider.PREPARE, true);
662 getContentResolver().insert(uri, values);
663 mSharingImage = true;
665 // Process and save the image in the background.
666 showSavingProgress(null);
667 mImageShow.saveImage(this, mSharedOutputFile);
671 private Intent getDefaultShareIntent() {
672 Intent intent = new Intent(Intent.ACTION_SEND);
673 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
674 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
675 intent.setType(SharedImageProvider.MIME_TYPE);
676 mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri());
677 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
678 Uri.encode(mSharedOutputFile.getAbsolutePath()));
679 intent.putExtra(Intent.EXTRA_STREAM, uri);
684 public boolean onCreateOptionsMenu(Menu menu) {
685 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
686 MenuItem showHistory = menu.findItem(R.id.operationsButton);
687 if (mShowingHistoryPanel) {
688 showHistory.setTitle(R.string.hide_history_panel);
690 showHistory.setTitle(R.string.show_history_panel);
692 MenuItem showState = menu.findItem(R.id.showImageStateButton);
693 if (mShowingImageStatePanel) {
694 showState.setTitle(R.string.hide_imagestate_panel);
696 showState.setTitle(R.string.show_imagestate_panel);
698 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
699 .getActionProvider();
700 mShareActionProvider.setShareIntent(getDefaultShareIntent());
701 mShareActionProvider.setOnShareTargetSelectedListener(this);
703 MenuItem undoItem = menu.findItem(R.id.undoButton);
704 MenuItem redoItem = menu.findItem(R.id.redoButton);
705 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
706 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
711 public void onPause() {
713 if (mShareActionProvider != null) {
714 mShareActionProvider.setOnShareTargetSelectedListener(null);
719 public void onResume() {
721 if (mShareActionProvider != null) {
722 mShareActionProvider.setOnShareTargetSelectedListener(this);
727 public boolean onOptionsItemSelected(MenuItem item) {
728 switch (item.getItemId()) {
729 case R.id.undoButton: {
730 mPanelController.resetParameters();
731 HistoryAdapter adapter = mMasterImage.getHistory();
732 int position = adapter.undo();
733 mMasterImage.onHistoryItemClick(position);
734 mImageShow.showToast("Undo");
738 case R.id.redoButton: {
739 HistoryAdapter adapter = mMasterImage.getHistory();
740 int position = adapter.redo();
741 mMasterImage.onHistoryItemClick(position);
742 mImageShow.showToast("Redo");
746 case R.id.resetHistoryButton: {
750 case R.id.showImageStateButton: {
751 toggleImageStatePanel();
754 case R.id.operationsButton: {
755 toggleHistoryPanel();
758 case android.R.id.home: {
766 public void enableSave(boolean enable) {
767 if (mSaveButton != null)
768 mSaveButton.setEnabled(enable);
771 public FilterIconButton setupFilterRepresentationButton(FilterRepresentation representation, LinearLayout panel, View button) {
772 LayoutInflater inflater =
773 (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
774 FilterIconButton icon = (FilterIconButton) inflater.inflate(R.layout.filtericonbutton,
776 if (representation.getTextId() != 0) {
777 representation.setName(getString(representation.getTextId()));
779 String text = representation.getName();
780 icon.setup(text, mPanelController, panel);
781 icon.setFilterRepresentation(representation);
782 icon.setId(representation.getEditorId());
783 mPanelController.addComponent(button, icon);
788 private void fillFx(LinearLayout listFilters, int buttonId) {
789 // TODO: use listview
790 // TODO: load the filters straight from the filesystem
792 FilterFxRepresentation[] fxArray = new FilterFxRepresentation[18];
796 R.drawable.filtershow_fx_0005_punch,
797 R.drawable.filtershow_fx_0000_vintage,
798 R.drawable.filtershow_fx_0004_bw_contrast,
799 R.drawable.filtershow_fx_0002_bleach,
800 R.drawable.filtershow_fx_0001_instant,
801 R.drawable.filtershow_fx_0007_washout,
802 R.drawable.filtershow_fx_0003_blue_crush,
803 R.drawable.filtershow_fx_0008_washout_color,
804 R.drawable.filtershow_fx_0006_x_process
809 R.string.ffx_vintage,
810 R.string.ffx_bw_contrast,
812 R.string.ffx_instant,
813 R.string.ffx_washout,
814 R.string.ffx_blue_crush,
815 R.string.ffx_washout_color,
816 R.string.ffx_x_process
819 for (int i = 0; i < drawid.length; i++) {
820 FilterFxRepresentation fx = new FilterFxRepresentation(getString(fxNameid[i]), drawid[i], fxNameid[i]);
824 ImageButton button = (ImageButton) findViewById(buttonId);
826 FilterFxRepresentation nullFx = new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
827 mNullFxFilter = setupFilterRepresentationButton(nullFx, listFilters, button);
828 mNullFxFilter.setSelected(true);
830 Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
831 FiltersManager.getManager().addLooks(filtersRepresentations);
832 for (FilterRepresentation representation : filtersRepresentations) {
833 setupFilterRepresentationButton(representation, listFilters, button);
836 for (int i = 0; i < p; i++) {
837 setupFilterRepresentationButton(fxArray[i], listFilters, button);
841 public void setDefaultPreset() {
842 // Default preset (original)
843 ImagePreset preset = new ImagePreset(getString(R.string.history_original)); // empty
844 preset.setImageLoader(mImageLoader);
846 mMasterImage.setPreset(preset, true);
849 // //////////////////////////////////////////////////////////////////////////////
850 // Some utility functions
851 // TODO: finish the cleanup.
853 public void invalidateViews() {
854 for (ImageShow views : mImageViews) {
860 public void hideImageViews() {
861 for (View view : mImageViews) {
862 view.setVisibility(View.GONE);
864 mEditorPlaceHolder.hide();
867 // //////////////////////////////////////////////////////////////////////////////
868 // imageState panel...
870 public boolean isShowingHistoryPanel() {
871 return mShowingHistoryPanel;
874 private void toggleImageStatePanel() {
875 final View viewList = findViewById(R.id.imageStatePanel);
877 if (mShowingHistoryPanel) {
878 findViewById(R.id.historyPanel).setVisibility(View.GONE);
879 mShowingHistoryPanel = false;
882 if (!mShowingImageStatePanel) {
883 mShowingImageStatePanel = true;
884 viewList.setVisibility(View.VISIBLE);
886 mShowingImageStatePanel = false;
887 viewList.setVisibility(View.GONE);
889 invalidateOptionsMenu();
893 public void onConfigurationChanged(Configuration newConfig)
895 super.onConfigurationChanged(newConfig);
898 if (getResources().getConfiguration().orientation
899 == Configuration.ORIENTATION_LANDSCAPE) {
900 mShowingImageStatePanel = true;
901 } else if (mShowingImageStatePanel) {
902 toggleImageStatePanel();
904 if (mShowingHistoryPanel) {
905 toggleHistoryPanel();
907 if (mShowingTinyPlanet == false) {
908 View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
909 if (tinyPlanetView != null) {
910 tinyPlanetView.setVisibility(View.GONE);
913 final View loading = findViewById(R.id.loading);
914 loading.setVisibility(View.GONE);
917 public void setupMasterImage() {
918 mImageLoader = new ImageLoader(this, getApplicationContext());
920 HistoryAdapter mHistoryAdapter = new HistoryAdapter(
921 this, R.layout.filtershow_history_operation_row,
923 ImageStateAdapter mImageStateAdapter = new ImageStateAdapter(this,
924 R.layout.filtershow_imagestate_row);
927 mMasterImage = MasterImage.getImage();
928 mMasterImage.setHistoryAdapter(mHistoryAdapter);
929 mMasterImage.setStateAdapter(mImageStateAdapter);
930 mMasterImage.setActivity(this);
931 mMasterImage.setImageLoader(mImageLoader);
934 // //////////////////////////////////////////////////////////////////////////////
937 public void toggleHistoryPanel() {
938 final View view = findViewById(R.id.mainPanel);
939 final View viewList = findViewById(R.id.historyPanel);
941 if (mShowingImageStatePanel) {
942 findViewById(R.id.imageStatePanel).setVisibility(View.GONE);
945 int translate = translateMainPanel(viewList);
946 if (!mShowingHistoryPanel) {
947 mShowingHistoryPanel = true;
948 if (getResources().getConfiguration().orientation
949 == Configuration.ORIENTATION_PORTRAIT) {
950 // If portrait, always remove the state panel
951 mShowingImageStatePanel = false;
952 if (PanelController.useAnimations()) {
953 view.animate().setDuration(200).x(translate)
954 .withLayer().withEndAction(new Runnable() {
957 viewList.setAlpha(0);
958 viewList.setVisibility(View.VISIBLE);
959 viewList.animate().setDuration(100)
960 .alpha(1.0f).start();
964 view.setX(translate);
965 viewList.setAlpha(0);
966 viewList.setVisibility(View.VISIBLE);
967 viewList.animate().setDuration(100)
968 .alpha(1.0f).start();
971 findViewById(R.id.filtersPanel).setVisibility(View.GONE);
972 viewList.setVisibility(View.VISIBLE);
975 mShowingHistoryPanel = false;
976 if (getResources().getConfiguration().orientation
977 == Configuration.ORIENTATION_PORTRAIT) {
978 viewList.setVisibility(View.INVISIBLE);
979 if (PanelController.useAnimations()) {
980 view.animate().setDuration(200).x(0).withLayer()
986 viewList.setVisibility(View.GONE);
987 findViewById(R.id.filtersPanel).setVisibility(View.VISIBLE);
988 // In landscape, bring back the state panel if it was there
989 if (mShowingImageStatePanel) {
990 findViewById(R.id.imageStatePanel).setVisibility(View.VISIBLE);
994 invalidateOptionsMenu();
997 public void dispatchNullFilterClick() {
998 mNullFxFilter.onClick(mNullFxFilter);
999 mNullBorderFilter.onClick(mNullBorderFilter);
1002 void resetHistory() {
1003 dispatchNullFilterClick();
1004 HistoryAdapter adapter = mMasterImage.getHistory();
1006 ImagePreset original = new ImagePreset(adapter.getItem(0));
1007 mMasterImage.setPreset(original, true);
1008 mPanelController.resetParameters();
1012 // reset button in the history panel.
1013 private OnClickListener createOnClickResetOperationsButton() {
1014 return new View.OnClickListener() {
1016 public void onClick(View v) {
1023 public void onBackPressed() {
1024 if (mPanelController.onBackPressed()) {
1029 public PanelController getPanelController() {
1030 return mPanelController;
1033 public void cannotLoadImage() {
1034 CharSequence text = getString(R.string.cannot_load_image);
1035 Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
1040 // //////////////////////////////////////////////////////////////////////////////
1042 public float getPixelsFromDip(float value) {
1043 Resources r = getResources();
1044 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1045 r.getDisplayMetrics());
1049 public void onItemClick(AdapterView<?> parent, View view, int position,
1051 mMasterImage.onHistoryItemClick(position);
1055 public void pickImage() {
1056 Intent intent = new Intent();
1057 intent.setType("image/*");
1058 intent.setAction(Intent.ACTION_GET_CONTENT);
1059 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1064 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1065 if (resultCode == RESULT_OK) {
1066 if (requestCode == SELECT_PICTURE) {
1067 Uri selectedImageUri = data.getData();
1068 startLoadBitmap(selectedImageUri);
1073 private boolean mSaveToExtraUri = false;
1074 private boolean mSaveAsWallpaper = false;
1075 private boolean mReturnAsExtra = false;
1076 private boolean mOutputted = false;
1078 public void saveImage() {
1079 if (mCropExtras != null) {
1080 if (mCropExtras.getExtraOutput() != null) {
1081 mSaveToExtraUri = true;
1084 if (mCropExtras.getSetAsWallpaper()) {
1085 mSaveAsWallpaper = true;
1088 if (mCropExtras.getReturnData()) {
1090 mReturnAsExtra = true;
1095 mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true);
1096 showSavingProgress(null);
1097 mImageShow.returnFilteredResult(this);
1101 if (mImageShow.hasModifications()) {
1102 // Get the name of the album, to which the image will be saved
1103 File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
1104 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1105 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1106 showSavingProgress(albumName);
1107 mImageShow.saveImage(this, null);
1114 public void onFilteredResult(Bitmap filtered) {
1115 Intent intent = new Intent();
1116 intent.putExtra(CropExtras.KEY_CROPPED_RECT, mImageShow.getImageCropBounds());
1117 if (mSaveToExtraUri) {
1118 mImageShow.saveToUri(filtered, mCropExtras.getExtraOutput(),
1119 mCropExtras.getOutputFormat(), this);
1121 if (mSaveAsWallpaper) {
1122 setWallpaperInBackground(filtered);
1124 if (mReturnAsExtra) {
1125 if (filtered != null) {
1126 int bmapSize = filtered.getRowBytes() * filtered.getHeight();
1128 * Max size of Binder transaction buffer is 1Mb, so constrain
1129 * Bitmap to be somewhat less than this, otherwise we get
1130 * TransactionTooLargeExceptions.
1132 if (bmapSize > MAX_BMAP_IN_INTENT) {
1133 Log.w(LOGTAG, "Bitmap too large to be returned via intent");
1135 intent.putExtra(CropExtras.KEY_DATA, filtered);
1139 setResult(RESULT_OK, intent);
1140 if (!mSaveToExtraUri) {
1145 void setWallpaperInBackground(final Bitmap bmap) {
1146 Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
1147 BitmapTask.Callbacks<FilterShowActivity> cb = new BitmapTask.Callbacks<FilterShowActivity>() {
1149 public void onComplete(Bitmap result) {}
1152 public void onCancel() {}
1155 public Bitmap onExecute(FilterShowActivity param) {
1157 WallpaperManager.getInstance(param).setBitmap(bmap);
1158 } catch (IOException e) {
1159 Log.w(LOGTAG, "fail to set wall paper", e);
1164 (new BitmapTask<FilterShowActivity>(cb)).execute(this);
1167 public void done() {
1169 hideSavingProgress();
1175 System.loadLibrary("jni_filtershow_filters");