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