FilterShowActivity.java revision d4653680279a85720f792f21e32578ccf5c6c34f
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package com.android.gallery3d.filtershow; 18 19import android.app.ActionBar; 20import android.app.Activity; 21import android.app.ProgressDialog; 22import android.app.WallpaperManager; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.Intent; 26import android.content.res.Configuration; 27import android.content.res.Resources; 28import android.graphics.Bitmap; 29import android.graphics.BitmapFactory; 30import android.graphics.Color; 31import android.graphics.Point; 32import android.graphics.drawable.Drawable; 33import android.net.Uri; 34import android.os.AsyncTask; 35import android.os.Bundle; 36import android.provider.MediaStore; 37import android.util.DisplayMetrics; 38import android.util.Log; 39import android.util.TypedValue; 40import android.view.Display; 41import android.view.LayoutInflater; 42import android.view.Menu; 43import android.view.MenuItem; 44import android.view.View; 45import android.view.View.OnClickListener; 46import android.view.WindowManager; 47import android.widget.AdapterView; 48import android.widget.AdapterView.OnItemClickListener; 49import android.widget.FrameLayout; 50import android.widget.ImageButton; 51import android.widget.LinearLayout; 52import android.widget.ListView; 53import android.widget.ShareActionProvider; 54import android.widget.ShareActionProvider.OnShareTargetSelectedListener; 55import android.widget.Toast; 56 57import com.android.gallery3d.R; 58import com.android.gallery3d.data.LocalAlbum; 59import com.android.gallery3d.filtershow.cache.FilteringPipeline; 60import com.android.gallery3d.filtershow.cache.ImageLoader; 61import com.android.gallery3d.filtershow.editors.BasicEditor; 62import com.android.gallery3d.filtershow.editors.EditorCrop; 63import com.android.gallery3d.filtershow.editors.EditorDraw; 64import com.android.gallery3d.filtershow.editors.EditorFlip; 65import com.android.gallery3d.filtershow.editors.EditorInfo; 66import com.android.gallery3d.filtershow.editors.EditorManager; 67import com.android.gallery3d.filtershow.editors.EditorRedEye; 68import com.android.gallery3d.filtershow.editors.EditorRotate; 69import com.android.gallery3d.filtershow.editors.EditorStraighten; 70import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; 71import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; 72import com.android.gallery3d.filtershow.filters.FilterColorBorderRepresentation; 73import com.android.gallery3d.filtershow.filters.FilterFxRepresentation; 74import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation; 75import com.android.gallery3d.filtershow.filters.FilterRepresentation; 76import com.android.gallery3d.filtershow.filters.FiltersManager; 77import com.android.gallery3d.filtershow.filters.ImageFilter; 78import com.android.gallery3d.filtershow.filters.ImageFilterBorder; 79import com.android.gallery3d.filtershow.filters.ImageFilterRS; 80import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; 81import com.android.gallery3d.filtershow.imageshow.ImageCrop; 82import com.android.gallery3d.filtershow.imageshow.ImageShow; 83import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet; 84import com.android.gallery3d.filtershow.imageshow.ImageZoom; 85import com.android.gallery3d.filtershow.imageshow.MasterImage; 86import com.android.gallery3d.filtershow.presets.ImagePreset; 87import com.android.gallery3d.filtershow.provider.SharedImageProvider; 88import com.android.gallery3d.filtershow.tools.BitmapTask; 89import com.android.gallery3d.filtershow.tools.SaveCopyTask; 90import com.android.gallery3d.filtershow.ui.FilterIconButton; 91import com.android.gallery3d.filtershow.ui.FramedTextButton; 92import com.android.gallery3d.filtershow.ui.Spline; 93import com.android.gallery3d.util.GalleryUtils; 94 95import java.io.File; 96import java.io.IOException; 97import java.lang.ref.WeakReference; 98import java.util.Vector; 99 100public class FilterShowActivity extends Activity implements OnItemClickListener, 101 OnShareTargetSelectedListener { 102 103 // fields for supporting crop action 104 public static final String CROP_ACTION = "com.android.camera.action.CROP"; 105 private CropExtras mCropExtras = null; 106 private String mAction = ""; 107 MasterImage mMasterImage = null; 108 109 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET"; 110 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen"; 111 public static final int MAX_BMAP_IN_INTENT = 990000; 112 private final PanelController mPanelController = new PanelController(); 113 private ImageLoader mImageLoader = null; 114 private ImageShow mImageShow = null; 115 private ImageTinyPlanet mImageTinyPlanet = null; 116 117 private View mSaveButton = null; 118 119 private ImageButton mFxButton = null; 120 private ImageButton mBorderButton = null; 121 private ImageButton mGeometryButton = null; 122 private ImageButton mColorsButton = null; 123 124 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this); 125 126 private static final int SELECT_PICTURE = 1; 127 private static final String LOGTAG = "FilterShowActivity"; 128 protected static final boolean ANIMATE_PANELS = true; 129 private static int mImageBorderSize = 4; // in percent 130 131 private boolean mShowingHistoryPanel = false; 132 private boolean mShowingImageStatePanel = false; 133 134 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>(); 135 136 private ShareActionProvider mShareActionProvider; 137 private File mSharedOutputFile = null; 138 139 private boolean mSharingImage = false; 140 141 private WeakReference<ProgressDialog> mSavingProgressDialog; 142 143 private LoadBitmapTask mLoadBitmapTask; 144 private FilterIconButton mNullFxFilter; 145 private FilterIconButton mNullBorderFilter; 146 private int mIconSeedSize = 140; 147 148 @Override 149 public void onCreate(Bundle savedInstanceState) { 150 super.onCreate(savedInstanceState); 151 ImageFilter.setActivityForMemoryToasts(this); 152 setResources(); 153 154 Resources res = getResources(); 155 setupMasterImage(); 156 ImageFilterRS.setRenderScriptContext(this); 157 158 ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen)); 159 // TODO: get those values from XML. 160 ImageZoom.setZoomedSize(getPixelsFromDip(256)); 161 FramedTextButton.setTextSize((int) getPixelsFromDip(14)); 162 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4)); 163 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10)); 164 ImageShow.setTextSize((int) getPixelsFromDip(12)); 165 ImageShow.setTextPadding((int) getPixelsFromDip(10)); 166 ImageShow.setOriginalTextMargin((int) getPixelsFromDip(4)); 167 ImageShow.setOriginalTextSize((int) getPixelsFromDip(18)); 168 ImageShow.setOriginalText(res.getString(R.string.original_picture_text)); 169 mIconSeedSize = res.getDimensionPixelSize(R.dimen.thumbnail_size); 170 171 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop); 172 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size); 173 Spline.setCurveHandle(curveHandle, curveHandleSize); 174 Spline.setCurveWidth((int) getPixelsFromDip(3)); 175 176 setContentView(R.layout.filtershow_activity); 177 ActionBar actionBar = getActionBar(); 178 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 179 actionBar.setCustomView(R.layout.filtershow_actionbar); 180 181 mSaveButton = actionBar.getCustomView(); 182 mSaveButton.setOnClickListener(new OnClickListener() { 183 @Override 184 public void onClick(View view) { 185 saveImage(); 186 } 187 }); 188 189 mImageLoader = new ImageLoader(this, getApplicationContext()); 190 mImageShow = (ImageShow) findViewById(R.id.imageShow); 191 mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet); 192 193 ImageCrop.setAspectTextSize((int) getPixelsFromDip(18)); 194 ImageCrop.setTouchTolerance((int) getPixelsFromDip(25)); 195 ImageCrop.setMinCropSize((int) getPixelsFromDip(55)); 196 mImageViews.add(mImageShow); 197 mImageViews.add(mImageTinyPlanet); 198 199 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer)); 200 mEditorPlaceHolder.addEditor(new EditorDraw()); 201 mEditorPlaceHolder.addEditor(new BasicEditor()); 202 mEditorPlaceHolder.addEditor(new ImageOnlyEditor()); 203 mEditorPlaceHolder.addEditor(new EditorTinyPlanet()); 204 mEditorPlaceHolder.addEditor(new EditorRedEye()); 205 mEditorPlaceHolder.addEditor(new EditorCrop()); 206 mEditorPlaceHolder.addEditor(new EditorFlip()); 207 mEditorPlaceHolder.addEditor(new EditorRotate()); 208 mEditorPlaceHolder.addEditor(new EditorStraighten()); 209 EditorManager.addEditors(mEditorPlaceHolder); 210 mEditorPlaceHolder.setOldViews(mImageViews); 211 mEditorPlaceHolder.setImageLoader(mImageLoader); 212 213 mEditorPlaceHolder.hide(); 214 215 mFxButton = (ImageButton) findViewById(R.id.fxButton); 216 mBorderButton = (ImageButton) findViewById(R.id.borderButton); 217 mGeometryButton = (ImageButton) findViewById(R.id.geometryButton); 218 mColorsButton = (ImageButton) findViewById(R.id.colorsButton); 219 220 mImageShow.setImageLoader(mImageLoader); 221 mImageTinyPlanet.setImageLoader(mImageLoader); 222 223 mPanelController.setActivity(this); 224 mPanelController.setEditorPlaceHolder(mEditorPlaceHolder); 225 226 mPanelController.addImageView(findViewById(R.id.imageShow)); 227 mPanelController.addImageView(findViewById(R.id.imageTinyPlanet)); 228 229 mPanelController.addPanel(mFxButton, findViewById(R.id.fxList), 0); 230 mPanelController.addPanel(mBorderButton, findViewById(R.id.bordersList), 1); 231 mPanelController.addPanel(mGeometryButton, findViewById(R.id.geometryList), 2); 232 233 // TODO: move to a separate function. 234 GeometryMetadata geo = new GeometryMetadata(); 235 int[] editorsId = geo.getEditorIds(); 236 for (int i = 0; i < editorsId.length; i++) { 237 int editorId = editorsId[i]; 238 GeometryMetadata geometry = new GeometryMetadata(geo); 239 geometry.setEditorId(editorId); 240 EditorInfo editorInfo = (EditorInfo) mEditorPlaceHolder.getEditor(editorId); 241 geometry.setTextId(editorInfo.getTextId()); 242 geometry.setOverlayId(editorInfo.getOverlayId()); 243 geometry.setOverlayOnly(editorInfo.getOverlayOnly()); 244 setupFilterRepresentationButton( 245 geometry, (LinearLayout) findViewById(R.id.listGeometry), mGeometryButton); 246 } 247 248 mPanelController.addPanel(mColorsButton, findViewById(R.id.colorsFxList), 3); 249 250 Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>(); 251 252 FiltersManager filtersManager = FiltersManager.getManager(); 253 filtersManager.addEffects(filtersRepresentations); 254 255 for (FilterRepresentation representation : filtersRepresentations) { 256 setupFilterRepresentationButton(representation, 257 (LinearLayout) findViewById(R.id.listColorsFx), mColorsButton); 258 } 259 260 mPanelController.addView(findViewById(R.id.applyEffect)); 261 findViewById(R.id.resetOperationsButton).setOnClickListener( 262 createOnClickResetOperationsButton()); 263 264 ListView operationsList = (ListView) findViewById(R.id.operationsList); 265 operationsList.setAdapter(mMasterImage.getHistory()); 266 operationsList.setOnItemClickListener(this); 267 ListView imageStateList = (ListView) findViewById(R.id.imageStateList); 268 imageStateList.setAdapter(mMasterImage.getState()); 269 mImageLoader.setAdapter(mMasterImage.getHistory()); 270 271 fillListImages((LinearLayout) findViewById(R.id.listFilters)); 272 LoadBordersTask loadBorders = new LoadBordersTask((LinearLayout) findViewById(R.id.listBorders)); 273 loadBorders.execute(); 274 275 mPanelController.setRowPanel(findViewById(R.id.secondRowPanel)); 276 mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList), 277 findViewById(R.id.panelAccessoryViewList), 278 findViewById(R.id.applyEffect)); 279 280 mPanelController.setCurrentPanel(mFxButton); 281 Intent intent = getIntent(); 282 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) { 283 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 284 } 285 286 mAction = intent.getAction(); 287 288 if (intent.getData() != null) { 289 startLoadBitmap(intent.getData()); 290 } else { 291 pickImage(); 292 } 293 294 // Handle behavior for various actions 295 if (mAction.equalsIgnoreCase(CROP_ACTION)) { 296 Bundle extras = intent.getExtras(); 297 if (extras != null) { 298 mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), 299 extras.getInt(CropExtras.KEY_OUTPUT_Y, 0), 300 extras.getBoolean(CropExtras.KEY_SCALE, true) && 301 extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false), 302 extras.getInt(CropExtras.KEY_ASPECT_X, 0), 303 extras.getInt(CropExtras.KEY_ASPECT_Y, 0), 304 extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false), 305 extras.getBoolean(CropExtras.KEY_RETURN_DATA, false), 306 (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT), 307 extras.getString(CropExtras.KEY_OUTPUT_FORMAT), 308 extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false), 309 extras.getFloat(CropExtras.KEY_SPOTLIGHT_X), 310 extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y)); 311 312 if (mCropExtras.getShowWhenLocked()) { 313 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 314 } 315 mImageShow.getImagePreset().mGeoData.setCropExtras(mCropExtras); 316 317 // FIXME: moving to editors breaks the crop action 318 EditorCrop crop = (EditorCrop) mEditorPlaceHolder.getEditor(EditorCrop.ID); 319 320 crop.setExtras(mCropExtras); 321 String s = getString(R.string.Fixed); 322 crop.setAspectString(s); 323 crop.setCropActionFlag(true); 324 mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0 325 && mCropExtras.getAspectY() > 0); 326 } 327 } 328 } 329 330 private void startLoadBitmap(Uri uri) { 331 final View filters = findViewById(R.id.filtersPanel); 332 final View loading = findViewById(R.id.loading); 333 final View imageShow = findViewById(R.id.imageShow); 334 imageShow.setVisibility(View.INVISIBLE); 335 filters.setVisibility(View.INVISIBLE); 336 loading.setVisibility(View.VISIBLE); 337 338 View tinyPlanetView = findViewById(EditorTinyPlanet.ID); 339 if (tinyPlanetView != null) { 340 tinyPlanetView.setVisibility(View.GONE); 341 } 342 mLoadBitmapTask = new LoadBitmapTask(tinyPlanetView); 343 mLoadBitmapTask.execute(uri); 344 } 345 346 private class LoadBordersTask extends AsyncTask<Void, Boolean, Boolean> { 347 Vector<FilterRepresentation> mBorders; 348 LinearLayout mList; 349 350 public LoadBordersTask(LinearLayout list) { 351 mList = list; 352 mBorders = new Vector<FilterRepresentation>(); 353 } 354 355 @Override 356 protected Boolean doInBackground(Void... params) { 357 mBorders.add(new FilterImageBorderRepresentation(0)); 358 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5)); 359 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush)); 360 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge)); 361 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e)); 362 mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape)); 363 mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0)); 364 mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, mImageBorderSize)); 365 mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0)); 366 mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, mImageBorderSize)); 367 int creamColor = Color.argb(255, 237, 237, 227); 368 mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0)); 369 mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, mImageBorderSize)); 370 return true; 371 } 372 373 @Override 374 protected void onPostExecute(Boolean result) { 375 if (!result) { 376 return; 377 } 378 for (int i = 0; i < mBorders.size(); i++) { 379 FilterRepresentation filter = mBorders.elementAt(i); 380 filter.setName(getString(R.string.borders)); 381 if (i == 0) { 382 filter.setName(getString(R.string.none)); 383 } 384 FilterIconButton b = setupFilterRepresentationButton(filter, mList, mBorderButton); 385 if (i == 0) { 386 mNullBorderFilter = b; 387 mNullBorderFilter.setSelected(true); 388 } 389 } 390 } 391 } 392 393 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> { 394 View mTinyPlanetButton; 395 int mBitmapSize; 396 397 public LoadBitmapTask(View button) { 398 mTinyPlanetButton = button; 399 mBitmapSize = getScreenImageSize(); 400 } 401 402 @Override 403 protected Boolean doInBackground(Uri... params) { 404 if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) { 405 return false; 406 } 407 publishProgress(mImageLoader.queryLightCycle360()); 408 return true; 409 } 410 411 @Override 412 protected void onProgressUpdate(Boolean... values) { 413 super.onProgressUpdate(values); 414 if (isCancelled()) { 415 return; 416 } 417 if (values[0]) { 418 mTinyPlanetButton.setVisibility(View.VISIBLE); 419 } 420 } 421 422 @Override 423 protected void onPostExecute(Boolean result) { 424 425 if (isCancelled()) { 426 return; 427 } 428 429 if (!result) { 430 cannotLoadImage(); 431 } 432 433 final View loading = findViewById(R.id.loading); 434 loading.setVisibility(View.GONE); 435 final View filters = findViewById(R.id.filtersPanel); 436 filters.setVisibility(View.VISIBLE); 437 if (PanelController.useAnimations()) { 438 float y = filters.getY(); 439 filters.setY(y + filters.getHeight()); 440 filters.animate().setDuration(600).y(y).withLayer().start(); 441 } 442 final View imageShow = findViewById(R.id.imageShow); 443 imageShow.setVisibility(View.VISIBLE); 444 445 Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge(); 446 FilteringPipeline pipeline = FilteringPipeline.getPipeline(); 447 pipeline.setOriginal(largeBitmap); 448 float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width(); 449 pipeline.setPreviewScaleFactor(previewScale); 450 451 Bitmap bmap = mImageLoader.getOriginalBitmapSmall(); 452 if (bmap != null && bmap.getWidth() > 0 && bmap.getHeight() > 0) { 453 float w = bmap.getWidth(); 454 float h = bmap.getHeight(); 455 float f = mIconSeedSize / Math.min(w, h); 456 w = w * f; 457 h = h * f; 458 bmap = Bitmap.createScaledBitmap(bmap, (int) w, (int) h, true); 459 460 LinearLayout listColors = (LinearLayout) findViewById(R.id.listColorsFx); 461 int num_colors_buttons = listColors.getChildCount(); 462 for (int i = 0; i < num_colors_buttons; i++) { 463 FilterIconButton b = (FilterIconButton) listColors.getChildAt(i); 464 b.setIcon(bmap); 465 } 466 467 LinearLayout listFilters = (LinearLayout) findViewById(R.id.listFilters); 468 int num_filters_buttons = listFilters.getChildCount(); 469 for (int i = 0; i < num_filters_buttons; i++) { 470 FilterIconButton b = (FilterIconButton) listFilters.getChildAt(i); 471 b.setIcon(bmap); 472 } 473 474 LinearLayout listBorders = (LinearLayout) findViewById(R.id.listBorders); 475 int num_borders_buttons = listBorders.getChildCount(); 476 for (int i = 0; i < num_borders_buttons; i++) { 477 FilterIconButton b = (FilterIconButton) listBorders.getChildAt(i); 478 b.setIcon(bmap); 479 } 480 481 } 482 MasterImage.getImage().setOriginalGeometry(largeBitmap); 483 mLoadBitmapTask = null; 484 485 if (mAction == CROP_ACTION) { 486 487 mPanelController.showComponent(findViewById(EditorCrop.ID)); 488 } else if (mAction == TINY_PLANET_ACTION) { 489 mPanelController.showComponent(findViewById(EditorTinyPlanet.ID)); 490 } 491 492 super.onPostExecute(result); 493 } 494 495 } 496 497 @Override 498 protected void onDestroy() { 499 if (mLoadBitmapTask != null) { 500 mLoadBitmapTask.cancel(false); 501 } 502 // TODO: Using singletons is a bad design choice for many of these 503 // due static reference leaks and in general. Please refactor. 504 MasterImage.reset(); 505 FilteringPipeline.reset(); 506 ImageFilter.resetStatics(); 507 FiltersManager.reset(); 508 super.onDestroy(); 509 } 510 511 private int translateMainPanel(View viewPanel) { 512 int accessoryPanelWidth = viewPanel.getWidth(); 513 int mainViewWidth = findViewById(R.id.mainView).getWidth(); 514 int mainPanelWidth = mImageShow.getDisplayedImageBounds().width(); 515 if (mainPanelWidth == 0) { 516 mainPanelWidth = mainViewWidth; 517 } 518 int filtersPanelWidth = findViewById(R.id.filtersPanel).getWidth(); 519 if (mainPanelWidth < filtersPanelWidth) { 520 mainPanelWidth = filtersPanelWidth; 521 } 522 int leftOver = mainViewWidth - mainPanelWidth - accessoryPanelWidth; 523 if (leftOver < 0) { 524 return -accessoryPanelWidth; 525 } 526 return 0; 527 } 528 529 private int getScreenImageSize() { 530 DisplayMetrics metrics = new DisplayMetrics(); 531 Display display = getWindowManager().getDefaultDisplay(); 532 Point size = new Point(); 533 display.getSize(size); 534 display.getMetrics(metrics); 535 int msize = Math.min(size.x, size.y); 536 return (133 * msize) / metrics.densityDpi; 537 } 538 539 private void showSavingProgress(String albumName) { 540 ProgressDialog progress; 541 if (mSavingProgressDialog != null) { 542 progress = mSavingProgressDialog.get(); 543 if (progress != null) { 544 progress.show(); 545 return; 546 } 547 } 548 // TODO: Allow cancellation of the saving process 549 String progressText; 550 if (albumName == null) { 551 progressText = getString(R.string.saving_image); 552 } else { 553 progressText = getString(R.string.filtershow_saving_image, albumName); 554 } 555 progress = ProgressDialog.show(this, "", progressText, true, false); 556 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress); 557 } 558 559 private void hideSavingProgress() { 560 if (mSavingProgressDialog != null) { 561 ProgressDialog progress = mSavingProgressDialog.get(); 562 if (progress != null) 563 progress.dismiss(); 564 } 565 } 566 567 public void completeSaveImage(Uri saveUri) { 568 if (mSharingImage && mSharedOutputFile != null) { 569 // Image saved, we unblock the content provider 570 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, 571 Uri.encode(mSharedOutputFile.getAbsolutePath())); 572 ContentValues values = new ContentValues(); 573 values.put(SharedImageProvider.PREPARE, false); 574 getContentResolver().insert(uri, values); 575 } 576 setResult(RESULT_OK, new Intent().setData(saveUri)); 577 hideSavingProgress(); 578 finish(); 579 } 580 581 @Override 582 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) { 583 // First, let's tell the SharedImageProvider that it will need to wait 584 // for the image 585 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, 586 Uri.encode(mSharedOutputFile.getAbsolutePath())); 587 ContentValues values = new ContentValues(); 588 values.put(SharedImageProvider.PREPARE, true); 589 getContentResolver().insert(uri, values); 590 mSharingImage = true; 591 592 // Process and save the image in the background. 593 showSavingProgress(null); 594 mImageShow.saveImage(this, mSharedOutputFile); 595 return true; 596 } 597 598 private Intent getDefaultShareIntent() { 599 Intent intent = new Intent(Intent.ACTION_SEND); 600 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 601 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 602 intent.setType(SharedImageProvider.MIME_TYPE); 603 mSharedOutputFile = SaveCopyTask.getNewFile(this, mImageLoader.getUri()); 604 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI, 605 Uri.encode(mSharedOutputFile.getAbsolutePath())); 606 intent.putExtra(Intent.EXTRA_STREAM, uri); 607 return intent; 608 } 609 610 @Override 611 public boolean onCreateOptionsMenu(Menu menu) { 612 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu); 613 MenuItem showHistory = menu.findItem(R.id.operationsButton); 614 if (mShowingHistoryPanel) { 615 showHistory.setTitle(R.string.hide_history_panel); 616 } else { 617 showHistory.setTitle(R.string.show_history_panel); 618 } 619 MenuItem showState = menu.findItem(R.id.showImageStateButton); 620 if (mShowingImageStatePanel) { 621 showState.setTitle(R.string.hide_imagestate_panel); 622 } else { 623 showState.setTitle(R.string.show_imagestate_panel); 624 } 625 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share) 626 .getActionProvider(); 627 mShareActionProvider.setShareIntent(getDefaultShareIntent()); 628 mShareActionProvider.setOnShareTargetSelectedListener(this); 629 630 MenuItem undoItem = menu.findItem(R.id.undoButton); 631 MenuItem redoItem = menu.findItem(R.id.redoButton); 632 MenuItem resetItem = menu.findItem(R.id.resetHistoryButton); 633 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem); 634 return true; 635 } 636 637 @Override 638 public void onPause() { 639 super.onPause(); 640 if (mShareActionProvider != null) { 641 mShareActionProvider.setOnShareTargetSelectedListener(null); 642 } 643 } 644 645 @Override 646 public void onResume() { 647 super.onResume(); 648 if (mShareActionProvider != null) { 649 mShareActionProvider.setOnShareTargetSelectedListener(this); 650 } 651 } 652 653 @Override 654 public boolean onOptionsItemSelected(MenuItem item) { 655 switch (item.getItemId()) { 656 case R.id.undoButton: { 657 HistoryAdapter adapter = mMasterImage.getHistory(); 658 int position = adapter.undo(); 659 mMasterImage.onHistoryItemClick(position); 660 mImageShow.showToast("Undo"); 661 invalidateViews(); 662 return true; 663 } 664 case R.id.redoButton: { 665 HistoryAdapter adapter = mMasterImage.getHistory(); 666 int position = adapter.redo(); 667 mMasterImage.onHistoryItemClick(position); 668 mImageShow.showToast("Redo"); 669 invalidateViews(); 670 return true; 671 } 672 case R.id.resetHistoryButton: { 673 resetHistory(); 674 return true; 675 } 676 case R.id.showImageStateButton: { 677 toggleImageStatePanel(); 678 return true; 679 } 680 case R.id.operationsButton: { 681 toggleHistoryPanel(); 682 return true; 683 } 684 case android.R.id.home: { 685 saveImage(); 686 return true; 687 } 688 } 689 return false; 690 } 691 692 public void enableSave(boolean enable) { 693 if (mSaveButton != null) 694 mSaveButton.setEnabled(enable); 695 } 696 697 public FilterIconButton setupFilterRepresentationButton(FilterRepresentation representation, LinearLayout panel, View button) { 698 LayoutInflater inflater = 699 (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 700 FilterIconButton icon = (FilterIconButton) inflater.inflate(R.layout.filtericonbutton, 701 panel, false); 702 if (representation.getTextId() != 0) { 703 representation.setName(getString(representation.getTextId())); 704 } 705 String text = representation.getName(); 706 icon.setup(text, mPanelController, panel); 707 icon.setFilterRepresentation(representation); 708 icon.setId(representation.getEditorId()); 709 mPanelController.addComponent(button, icon); 710 panel.addView(icon); 711 return icon; 712 } 713 714 private void fillListImages(LinearLayout listFilters) { 715 // TODO: use listview 716 // TODO: load the filters straight from the filesystem 717 718 FilterFxRepresentation[] fxArray = new FilterFxRepresentation[18]; 719 int p = 0; 720 721 int[] drawid = { 722 R.drawable.filtershow_fx_0005_punch, 723 R.drawable.filtershow_fx_0000_vintage, 724 R.drawable.filtershow_fx_0004_bw_contrast, 725 R.drawable.filtershow_fx_0002_bleach, 726 R.drawable.filtershow_fx_0001_instant, 727 R.drawable.filtershow_fx_0007_washout, 728 R.drawable.filtershow_fx_0003_blue_crush, 729 R.drawable.filtershow_fx_0008_washout_color, 730 R.drawable.filtershow_fx_0006_x_process 731 }; 732 733 int[] fxNameid = { 734 R.string.ffx_punch, 735 R.string.ffx_vintage, 736 R.string.ffx_bw_contrast, 737 R.string.ffx_bleach, 738 R.string.ffx_instant, 739 R.string.ffx_washout, 740 R.string.ffx_blue_crush, 741 R.string.ffx_washout_color, 742 R.string.ffx_x_process 743 }; 744 745 ImagePreset preset = new ImagePreset(getString(R.string.history_original)); // empty 746 preset.setImageLoader(mImageLoader); 747 748 BitmapFactory.Options o = new BitmapFactory.Options(); 749 o.inScaled = false; 750 751 for (int i = 0; i < drawid.length; i++) { 752 Bitmap b = BitmapFactory.decodeResource(getResources(), drawid[i], o); 753 FilterFxRepresentation fx = new FilterFxRepresentation(getString(fxNameid[i]), drawid[i], fxNameid[i]); 754 fx.setFxBitmap(b); 755 fxArray[p++] = fx; 756 } 757 758 FilterFxRepresentation nullFx = new FilterFxRepresentation(getString(R.string.none), 0, R.string.none); 759 mNullFxFilter = setupFilterRepresentationButton(nullFx, listFilters, mFxButton); 760 mNullFxFilter.setSelected(true); 761 762 Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>(); 763 FiltersManager.getManager().addLooks(filtersRepresentations); 764 for (FilterRepresentation representation : filtersRepresentations) { 765 setupFilterRepresentationButton(representation, listFilters, mFxButton); 766 } 767 768 for (int i = 0; i < p; i++) { 769 setupFilterRepresentationButton(fxArray[i], listFilters, mFxButton); 770 } 771 772 // Default preset (original) 773 mMasterImage.setPreset(preset, true); 774 } 775 776 // ////////////////////////////////////////////////////////////////////////////// 777 // Some utility functions 778 // TODO: finish the cleanup. 779 780 public void invalidateViews() { 781 for (ImageShow views : mImageViews) { 782 views.invalidate(); 783 views.updateImage(); 784 } 785 } 786 787 public void hideImageViews() { 788 for (View view : mImageViews) { 789 view.setVisibility(View.GONE); 790 } 791 mEditorPlaceHolder.hide(); 792 } 793 794 // ////////////////////////////////////////////////////////////////////////////// 795 // imageState panel... 796 797 public boolean isShowingHistoryPanel() { 798 return mShowingHistoryPanel; 799 } 800 801 private void toggleImageStatePanel() { 802 final View view = findViewById(R.id.mainPanel); 803 final View viewList = findViewById(R.id.imageStatePanel); 804 805 if (mShowingHistoryPanel) { 806 findViewById(R.id.historyPanel).setVisibility(View.INVISIBLE); 807 mShowingHistoryPanel = false; 808 } 809 810 int translate = translateMainPanel(viewList); 811 if (!mShowingImageStatePanel) { 812 mShowingImageStatePanel = true; 813 if (PanelController.useAnimations()) { 814 view.animate().setDuration(200).x(translate) 815 .withLayer().withEndAction(new Runnable() { 816 @Override 817 public void run() { 818 viewList.setAlpha(0); 819 viewList.setVisibility(View.VISIBLE); 820 viewList.animate().setDuration(100) 821 .alpha(1.0f).start(); 822 } 823 }).start(); 824 } else { 825 view.setX(translate); 826 viewList.setAlpha(0); 827 viewList.setVisibility(View.VISIBLE); 828 viewList.animate().setDuration(100) 829 .alpha(1.0f).start(); 830 } 831 } else { 832 mShowingImageStatePanel = false; 833 viewList.setVisibility(View.INVISIBLE); 834 if (PanelController.useAnimations()) { 835 view.animate().setDuration(200).x(0).withLayer() 836 .start(); 837 } else { 838 view.setX(0); 839 } 840 } 841 invalidateOptionsMenu(); 842 } 843 844 @Override 845 public void onConfigurationChanged(Configuration newConfig) 846 { 847 super.onConfigurationChanged(newConfig); 848 setResources(); 849 if (mShowingHistoryPanel) { 850 toggleHistoryPanel(); 851 } 852 } 853 854 public void setupMasterImage() { 855 HistoryAdapter mHistoryAdapter = new HistoryAdapter( 856 this, R.layout.filtershow_history_operation_row, 857 R.id.rowTextView); 858 ImageStateAdapter mImageStateAdapter = new ImageStateAdapter(this, 859 R.layout.filtershow_imagestate_row); 860 861 MasterImage.reset(); 862 mMasterImage = MasterImage.getImage(); 863 mMasterImage.setHistoryAdapter(mHistoryAdapter); 864 mMasterImage.setStateAdapter(mImageStateAdapter); 865 mMasterImage.setActivity(this); 866 } 867 868 // ////////////////////////////////////////////////////////////////////////////// 869 // history panel... 870 871 public void toggleHistoryPanel() { 872 final View view = findViewById(R.id.mainPanel); 873 final View viewList = findViewById(R.id.historyPanel); 874 875 if (mShowingImageStatePanel) { 876 findViewById(R.id.imageStatePanel).setVisibility(View.INVISIBLE); 877 mShowingImageStatePanel = false; 878 } 879 880 int translate = translateMainPanel(viewList); 881 if (!mShowingHistoryPanel) { 882 mShowingHistoryPanel = true; 883 if (PanelController.useAnimations()) { 884 view.animate().setDuration(200).x(translate) 885 .withLayer().withEndAction(new Runnable() { 886 @Override 887 public void run() { 888 viewList.setAlpha(0); 889 viewList.setVisibility(View.VISIBLE); 890 viewList.animate().setDuration(100) 891 .alpha(1.0f).start(); 892 } 893 }).start(); 894 } else { 895 view.setX(translate); 896 viewList.setAlpha(0); 897 viewList.setVisibility(View.VISIBLE); 898 viewList.animate().setDuration(100) 899 .alpha(1.0f).start(); 900 } 901 } else { 902 mShowingHistoryPanel = false; 903 viewList.setVisibility(View.INVISIBLE); 904 if (PanelController.useAnimations()) { 905 view.animate().setDuration(200).x(0).withLayer() 906 .start(); 907 } else { 908 view.setX(0); 909 } 910 } 911 invalidateOptionsMenu(); 912 } 913 914 public void dispatchNullFilterClick() { 915 mNullFxFilter.onClick(mNullFxFilter); 916 mNullBorderFilter.onClick(mNullBorderFilter); 917 } 918 919 void resetHistory() { 920 dispatchNullFilterClick(); 921 HistoryAdapter adapter = mMasterImage.getHistory(); 922 adapter.reset(); 923 ImagePreset original = new ImagePreset(adapter.getItem(0)); 924 mMasterImage.setPreset(original, true); 925 mPanelController.resetParameters(); 926 invalidateViews(); 927 } 928 929 // reset button in the history panel. 930 private OnClickListener createOnClickResetOperationsButton() { 931 return new View.OnClickListener() { 932 @Override 933 public void onClick(View v) { 934 resetHistory(); 935 } 936 }; 937 } 938 939 @Override 940 public void onBackPressed() { 941 if (mPanelController.onBackPressed()) { 942 saveImage(); 943 } 944 } 945 946 public void cannotLoadImage() { 947 CharSequence text = getString(R.string.cannot_load_image); 948 Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT); 949 toast.show(); 950 finish(); 951 } 952 953 // ////////////////////////////////////////////////////////////////////////////// 954 955 public float getPixelsFromDip(float value) { 956 Resources r = getResources(); 957 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, 958 r.getDisplayMetrics()); 959 } 960 961 @Override 962 public void onItemClick(AdapterView<?> parent, View view, int position, 963 long id) { 964 mMasterImage.onHistoryItemClick(position); 965 invalidateViews(); 966 } 967 968 public void pickImage() { 969 Intent intent = new Intent(); 970 intent.setType("image/*"); 971 intent.setAction(Intent.ACTION_GET_CONTENT); 972 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)), 973 SELECT_PICTURE); 974 } 975 976 @Override 977 public void onActivityResult(int requestCode, int resultCode, Intent data) { 978 if (resultCode == RESULT_OK) { 979 if (requestCode == SELECT_PICTURE) { 980 Uri selectedImageUri = data.getData(); 981 startLoadBitmap(selectedImageUri); 982 } 983 } 984 } 985 986 private boolean mSaveToExtraUri = false; 987 private boolean mSaveAsWallpaper = false; 988 private boolean mReturnAsExtra = false; 989 private boolean mOutputted = false; 990 991 public void saveImage() { 992 if (mCropExtras != null) { 993 if (mCropExtras.getExtraOutput() != null) { 994 mSaveToExtraUri = true; 995 mOutputted = true; 996 } 997 if (mCropExtras.getSetAsWallpaper()) { 998 mSaveAsWallpaper = true; 999 mOutputted = true; 1000 } 1001 if (mCropExtras.getReturnData()) { 1002 1003 mReturnAsExtra = true; 1004 mOutputted = true; 1005 } 1006 1007 if (mOutputted) { 1008 mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true); 1009 showSavingProgress(null); 1010 mImageShow.returnFilteredResult(this); 1011 } 1012 } 1013 if (!mOutputted) { 1014 if (mImageShow.hasModifications()) { 1015 // Get the name of the album, to which the image will be saved 1016 File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri()); 1017 int bucketId = GalleryUtils.getBucketId(saveDir.getPath()); 1018 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null); 1019 showSavingProgress(albumName); 1020 mImageShow.saveImage(this, null); 1021 } else { 1022 done(); 1023 } 1024 } 1025 } 1026 1027 public void onFilteredResult(Bitmap filtered) { 1028 Intent intent = new Intent(); 1029 intent.putExtra(CropExtras.KEY_CROPPED_RECT, mImageShow.getImageCropBounds()); 1030 if (mSaveToExtraUri) { 1031 mImageShow.saveToUri(filtered, mCropExtras.getExtraOutput(), 1032 mCropExtras.getOutputFormat(), this); 1033 } 1034 if (mSaveAsWallpaper) { 1035 setWallpaperInBackground(filtered); 1036 } 1037 if (mReturnAsExtra) { 1038 if (filtered != null) { 1039 int bmapSize = filtered.getRowBytes() * filtered.getHeight(); 1040 /* 1041 * Max size of Binder transaction buffer is 1Mb, so constrain 1042 * Bitmap to be somewhat less than this, otherwise we get 1043 * TransactionTooLargeExceptions. 1044 */ 1045 if (bmapSize > MAX_BMAP_IN_INTENT) { 1046 Log.w(LOGTAG, "Bitmap too large to be returned via intent"); 1047 } else { 1048 intent.putExtra(CropExtras.KEY_DATA, filtered); 1049 } 1050 } 1051 } 1052 setResult(RESULT_OK, intent); 1053 if (!mSaveToExtraUri) { 1054 done(); 1055 } 1056 } 1057 1058 void setWallpaperInBackground(final Bitmap bmap) { 1059 Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show(); 1060 BitmapTask.Callbacks<FilterShowActivity> cb = new BitmapTask.Callbacks<FilterShowActivity>() { 1061 @Override 1062 public void onComplete(Bitmap result) {} 1063 1064 @Override 1065 public void onCancel() {} 1066 1067 @Override 1068 public Bitmap onExecute(FilterShowActivity param) { 1069 try { 1070 WallpaperManager.getInstance(param).setBitmap(bmap); 1071 } catch (IOException e) { 1072 Log.w(LOGTAG, "fail to set wall paper", e); 1073 } 1074 return null; 1075 } 1076 }; 1077 (new BitmapTask<FilterShowActivity>(cb)).execute(this); 1078 } 1079 1080 public void done() { 1081 if (mOutputted) { 1082 hideSavingProgress(); 1083 } 1084 finish(); 1085 } 1086 1087 private void setResources() { 1088 ImageFilterBorder filterBorder = (ImageFilterBorder) FiltersManager.getManager().getFilter(ImageFilterBorder.class); 1089 filterBorder.setResources(getResources()); 1090 } 1091 1092 static { 1093 System.loadLibrary("jni_filtershow_filters"); 1094 } 1095 1096} 1097