1 /* 2 * Copyright (C) 2007 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 17 package com.android.camera; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.graphics.Bitmap; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.provider.MediaStore; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.GestureDetector; 29 import android.view.KeyEvent; 30 import android.view.Menu; 31 import android.view.MenuItem; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.Window; 35 import android.view.WindowManager; 36 import android.view.View.OnTouchListener; 37 import android.view.animation.AlphaAnimation; 38 import android.view.animation.Animation; 39 import android.widget.Toast; 40 import android.widget.ZoomButtonsController; 41 42 import com.android.camera.gallery.IImage; 43 import com.android.camera.gallery.IImageList; 44 import com.android.camera.gallery.VideoObject; 45 46 // This activity can display a whole picture and navigate them in a specific 47 // gallery. It has two modes: normal mode and slide show mode. In normal mode 48 // the user view one image at a time, and can click "previous" and "next" 49 // button to see the previous or next image. In slide show mode it shows one 50 // image after another, with some transition effect. 51 public class ReviewImage extends Activity implements View.OnClickListener { 52 private static final String STATE_URI = "uri"; 53 private static final String TAG = "ReviewImage"; 54 private static final double ASPECT_RATIO = 4.0 / 3.0; 55 56 private ImageGetter mGetter; 57 private Uri mSavedUri; 58 boolean mPaused = true; 59 private boolean mShowControls = true; 60 61 // Choices for what adjacents to load. 62 private static final int[] sOrderAdjacents = new int[] {0, 1, -1}; 63 64 final GetterHandler mHandler = new GetterHandler(); 65 66 private boolean mFullScreenInNormalMode; 67 68 int mCurrentPosition = 0; 69 70 private View mRootView; 71 private View mControlBar; 72 private View mNextImageView; 73 private View mPrevImageView; 74 private final Animation mHideNextImageViewAnimation = 75 new AlphaAnimation(1F, 0F); 76 private final Animation mHidePrevImageViewAnimation = 77 new AlphaAnimation(1F, 0F); 78 private final Animation mShowNextImageViewAnimation = 79 new AlphaAnimation(0F, 1F); 80 private final Animation mShowPrevImageViewAnimation = 81 new AlphaAnimation(0F, 1F); 82 83 public static final String KEY_IMAGE_LIST = "image_list"; 84 private static final String STATE_SHOW_CONTROLS = "show_controls"; 85 86 ImageManager.ImageListParam mParam; 87 IImageList mAllImages; 88 89 private final ImageViewTouchBase [] mSlideShowImageViews = 90 new ImageViewTouchBase[2]; 91 92 GestureDetector mGestureDetector; 93 private ZoomButtonsController mZoomButtonsController; 94 95 // The image view displayed for normal mode. 96 private ImageViewTouch2 mImageView; 97 // This is the cache for thumbnail bitmaps. 98 private BitmapCache mCache; 99 private MenuHelper.MenuItemsResult mImageMenuRunnable; 100 private final Runnable mDismissOnScreenControlRunner = new Runnable() { 101 public void run() { 102 hideOnScreenControls(); 103 } 104 }; 105 updateNextPrevControls()106 private void updateNextPrevControls() { 107 boolean showPrev = mCurrentPosition > 0; 108 boolean showNext = mCurrentPosition < mAllImages.getCount() - 1; 109 110 boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE; 111 boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE; 112 113 if (showPrev && !prevIsVisible) { 114 Animation a = mShowPrevImageViewAnimation; 115 a.setDuration(500); 116 mPrevImageView.startAnimation(a); 117 mPrevImageView.setVisibility(View.VISIBLE); 118 } else if (!showPrev && prevIsVisible) { 119 Animation a = mHidePrevImageViewAnimation; 120 a.setDuration(500); 121 mPrevImageView.startAnimation(a); 122 mPrevImageView.setVisibility(View.GONE); 123 } 124 125 if (showNext && !nextIsVisible) { 126 Animation a = mShowNextImageViewAnimation; 127 a.setDuration(500); 128 mNextImageView.startAnimation(a); 129 mNextImageView.setVisibility(View.VISIBLE); 130 } else if (!showNext && nextIsVisible) { 131 Animation a = mHideNextImageViewAnimation; 132 a.setDuration(500); 133 mNextImageView.startAnimation(a); 134 mNextImageView.setVisibility(View.GONE); 135 } 136 } 137 138 private void showOnScreenControls() { 139 140 // If the view has not been attached to the window yet, the 141 // zoomButtonControls will not able to show up. So delay it until the 142 // view has attached to window. 143 if (mRootView.getWindowToken() == null) { 144 mHandler.postGetterCallback(new Runnable() { 145 public void run() { 146 showOnScreenControls(); 147 } 148 }); 149 return; 150 } 151 152 // we may need to update the next/prev button due to index changing 153 updateNextPrevControls(); 154 155 if (ImageManager.isImage(mAllImages.getImageAt(mCurrentPosition))) { 156 updateZoomButtonsEnabled(); 157 mZoomButtonsController.setVisible(true); 158 } else { 159 mZoomButtonsController.setVisible(false); 160 } 161 } 162 163 private void hideOnScreenControls() { 164 if (mNextImageView.getVisibility() == View.VISIBLE) { 165 Animation a = mHideNextImageViewAnimation; 166 a.setDuration(500); 167 mNextImageView.startAnimation(a); 168 mNextImageView.setVisibility(View.INVISIBLE); 169 } 170 171 if (mPrevImageView.getVisibility() == View.VISIBLE) { 172 Animation a = mHidePrevImageViewAnimation; 173 a.setDuration(500); 174 mPrevImageView.startAnimation(a); 175 mPrevImageView.setVisibility(View.INVISIBLE); 176 } 177 178 mZoomButtonsController.setVisible(false); 179 } 180 181 private void scheduleDismissOnScreenControls() { 182 mHandler.removeCallbacks(mDismissOnScreenControlRunner); 183 mHandler.postDelayed(mDismissOnScreenControlRunner, 2000); 184 } 185 186 @Override 187 public boolean dispatchTouchEvent(MotionEvent m) { 188 if (mZoomButtonsController.isVisible()) { 189 scheduleDismissOnScreenControls(); 190 } 191 return super.dispatchTouchEvent(m); 192 } 193 194 private void updateZoomButtonsEnabled() { 195 ImageViewTouch2 imageView = mImageView; 196 float scale = imageView.getScale(); 197 mZoomButtonsController.setZoomInEnabled(scale < imageView.mMaxZoom); 198 mZoomButtonsController.setZoomOutEnabled(scale > 1); 199 } 200 201 @Override 202 protected void onDestroy() { 203 // This is necessary to make the ZoomButtonsController unregister 204 // its configuration change receiver. 205 if (mZoomButtonsController != null) { 206 mZoomButtonsController.setVisible(false); 207 } 208 209 super.onDestroy(); 210 } 211 212 private void setupOnScreenControls(View rootView, View ownerView) { 213 mNextImageView = rootView.findViewById(R.id.next_image); 214 mPrevImageView = rootView.findViewById(R.id.prev_image); 215 216 mNextImageView.setOnClickListener(this); 217 mPrevImageView.setOnClickListener(this); 218 219 setupZoomButtonController(ownerView); 220 setupOnTouchListeners(rootView); 221 } 222 223 private void setupZoomButtonController(final View ownerView) { 224 mZoomButtonsController = new ZoomButtonsController(ownerView); 225 mZoomButtonsController.setAutoDismissed(false); 226 mZoomButtonsController.setZoomSpeed(100); 227 mZoomButtonsController.setOnZoomListener( 228 new ZoomButtonsController.OnZoomListener() { 229 public void onVisibilityChanged(boolean visible) { 230 if (visible) { 231 updateZoomButtonsEnabled(); 232 } 233 } 234 235 public void onZoom(boolean zoomIn) { 236 if (zoomIn) { 237 mImageView.zoomIn(); 238 } else { 239 mImageView.zoomOut(); 240 } 241 mZoomButtonsController.setVisible(true); 242 updateZoomButtonsEnabled(); 243 } 244 }); 245 } 246 247 private void setupOnTouchListeners(View rootView) { 248 mGestureDetector = new GestureDetector(this, new MyGestureListener()); 249 250 // If the user touches anywhere on the panel (including the 251 // next/prev button). We show the on-screen controls. In addition 252 // to that, if the touch is not on the prev/next button, we 253 // pass the event to the gesture detector to detect double tap. 254 final OnTouchListener buttonListener = new OnTouchListener() { 255 public boolean onTouch(View v, MotionEvent event) { 256 scheduleDismissOnScreenControls(); 257 return false; 258 } 259 }; 260 261 OnTouchListener rootListener = new OnTouchListener() { 262 public boolean onTouch(View v, MotionEvent event) { 263 buttonListener.onTouch(v, event); 264 mGestureDetector.onTouchEvent(event); 265 266 // We do not use the return value of 267 // mGestureDetector.onTouchEvent because we will not receive 268 // the "up" event if we return false for the "down" event. 269 return true; 270 } 271 }; 272 273 mNextImageView.setOnTouchListener(buttonListener); 274 mPrevImageView.setOnTouchListener(buttonListener); 275 rootView.setOnTouchListener(rootListener); 276 } 277 278 private class MyGestureListener extends 279 GestureDetector.SimpleOnGestureListener { 280 281 @Override 282 public boolean onScroll(MotionEvent e1, MotionEvent e2, 283 float distanceX, float distanceY) { 284 ImageViewTouch2 imageView = mImageView; 285 if (imageView.getScale() > 1F) { 286 imageView.postTranslateCenter(-distanceX, -distanceY); 287 } 288 return true; 289 } 290 291 @Override onSingleTapConfirmed(MotionEvent e)292 public boolean onSingleTapConfirmed(MotionEvent e) { 293 showOnScreenControls(); 294 scheduleDismissOnScreenControls(); 295 return true; 296 } 297 298 @Override onDoubleTap(MotionEvent e)299 public boolean onDoubleTap(MotionEvent e) { 300 ImageViewTouch2 imageView = mImageView; 301 302 // Switch between the original scale and 3x scale. 303 if (imageView.getScale() > 2F) { 304 mImageView.zoomTo(1f); 305 } else { 306 mImageView.zoomToPoint(3f, e.getX(), e.getY()); 307 } 308 return true; 309 } 310 } 311 isPickIntent()312 boolean isPickIntent() { 313 String action = getIntent().getAction(); 314 return (Intent.ACTION_PICK.equals(action) 315 || Intent.ACTION_GET_CONTENT.equals(action)); 316 } 317 318 @Override onCreateOptionsMenu(Menu menu)319 public boolean onCreateOptionsMenu(Menu menu) { 320 super.onCreateOptionsMenu(menu); 321 322 // These are the menu items already covered by the side buttons. 323 int sideButtons = MenuHelper.INCLUDE_DELETE_MENU 324 | MenuHelper.INCLUDE_SHARE_MENU 325 | MenuHelper.INCLUDE_SET_MENU 326 | MenuHelper.INCLUDE_VIEWPLAY_MENU; 327 328 mImageMenuRunnable = MenuHelper.addImageMenuItems( 329 menu, 330 MenuHelper.INCLUDE_ALL & ~sideButtons, 331 ReviewImage.this, 332 mHandler, 333 mDeletePhotoRunnable, 334 new MenuHelper.MenuInvoker() { 335 public void run(final MenuHelper.MenuCallback cb) { 336 if (mPaused) return; 337 IImage image = mAllImages.getImageAt(mCurrentPosition); 338 Uri uri = image.fullSizeImageUri(); 339 cb.run(uri, image); 340 341 mImageView.clear(); 342 setImage(mCurrentPosition, false); 343 } 344 }); 345 346 return true; 347 } 348 349 protected Runnable mDeletePhotoRunnable = new Runnable() { 350 public void run() { 351 mAllImages.removeImageAt(mCurrentPosition); 352 if (mAllImages.getCount() == 0) { 353 finish(); 354 return; 355 } else { 356 if (mCurrentPosition == mAllImages.getCount()) { 357 mCurrentPosition -= 1; 358 } 359 } 360 mImageView.clear(); 361 mCache.clear(); // Because the position number is changed. 362 setImage(mCurrentPosition, true); 363 } 364 }; 365 366 @Override onPrepareOptionsMenu(Menu menu)367 public boolean onPrepareOptionsMenu(Menu menu) { 368 super.onPrepareOptionsMenu(menu); 369 if (mPaused) return false; 370 371 IImage image = mAllImages.getImageAt(mCurrentPosition); 372 if (mImageMenuRunnable != null) { 373 mImageMenuRunnable.gettingReadyToOpen(menu, image); 374 } 375 376 Uri uri = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri(); 377 MenuHelper.enableShareMenuItem(menu, MenuHelper.isWhiteListUri(uri)); 378 379 MenuHelper.enableShowOnMapMenuItem(menu, MenuHelper.hasLatLngData(image)); 380 381 return true; 382 } 383 384 @Override onMenuItemSelected(int featureId, MenuItem item)385 public boolean onMenuItemSelected(int featureId, MenuItem item) { 386 boolean b = super.onMenuItemSelected(featureId, item); 387 if (mImageMenuRunnable != null) { 388 mImageMenuRunnable.aboutToCall(item, 389 mAllImages.getImageAt(mCurrentPosition)); 390 } 391 return b; 392 } 393 setImage(int pos, boolean showControls)394 void setImage(int pos, boolean showControls) { 395 mCurrentPosition = pos; 396 397 Bitmap b = mCache.getBitmap(pos); 398 if (b != null) { 399 IImage image = mAllImages.getImageAt(pos); 400 mImageView.setImageRotateBitmapResetBase( 401 new RotateBitmap(b, image.getDegreesRotated()), true); 402 updateZoomButtonsEnabled(); 403 } 404 405 ImageGetterCallback cb = new ImageGetterCallback() { 406 public void completed() { 407 } 408 409 public boolean wantsThumbnail(int pos, int offset) { 410 return !mCache.hasBitmap(pos + offset); 411 } 412 413 public boolean wantsFullImage(int pos, int offset) { 414 return offset == 0; 415 } 416 417 public int fullImageSizeToUse(int pos, int offset) { 418 // this number should be bigger so that we can zoom. we may 419 // need to get fancier and read in the fuller size image as the 420 // user starts to zoom. 421 // Originally the value is set to 480 in order to avoid OOM. 422 // Now we set it to 2048 because of using 423 // native memory allocation for Bitmaps. 424 final int imageViewSize = 2048; 425 return imageViewSize; 426 } 427 428 public int [] loadOrder() { 429 return sOrderAdjacents; 430 } 431 432 public void imageLoaded(int pos, int offset, RotateBitmap bitmap, 433 boolean isThumb) { 434 // shouldn't get here after onPause() 435 436 // We may get a result from a previous request. Ignore it. 437 if (pos != mCurrentPosition) { 438 bitmap.recycle(); 439 return; 440 } 441 442 if (isThumb) { 443 mCache.put(pos + offset, bitmap.getBitmap()); 444 } 445 if (offset == 0) { 446 // isThumb: We always load thumb bitmap first, so we will 447 // reset the supp matrix for then thumb bitmap, and keep 448 // the supp matrix when the full bitmap is loaded. 449 mImageView.setImageRotateBitmapResetBase(bitmap, isThumb); 450 updateZoomButtonsEnabled(); 451 } 452 } 453 }; 454 455 // Could be null if we're stopping a slide show in the course of pausing 456 if (mGetter != null) { 457 mGetter.setPosition(pos, cb, mAllImages, mHandler); 458 } 459 updateActionIcons(); 460 if (showControls) showOnScreenControls(); 461 scheduleDismissOnScreenControls(); 462 } 463 464 @Override onCreate(Bundle instanceState)465 public void onCreate(Bundle instanceState) { 466 super.onCreate(instanceState); 467 468 Intent intent = getIntent(); 469 mFullScreenInNormalMode = intent.getBooleanExtra( 470 MediaStore.EXTRA_FULL_SCREEN, true); 471 472 setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); 473 requestWindowFeature(Window.FEATURE_NO_TITLE); 474 setContentView(R.layout.review_image); 475 476 mRootView = findViewById(R.id.root); 477 mControlBar = findViewById(R.id.control_bar); 478 mImageView = (ImageViewTouch2) findViewById(R.id.image); 479 mImageView.setEnableTrackballScroll(true); 480 mCache = new BitmapCache(3); 481 mImageView.setRecycler(mCache); 482 483 ((PreviewFrameLayout) findViewById( 484 R.id.frame_layout)).setAspectRatio(ASPECT_RATIO); 485 486 487 makeGetter(); 488 489 mSlideShowImageViews[0] = 490 (ImageViewTouchBase) findViewById(R.id.image1_slideShow); 491 mSlideShowImageViews[1] = 492 (ImageViewTouchBase) findViewById(R.id.image2_slideShow); 493 for (ImageViewTouchBase v : mSlideShowImageViews) { 494 v.setVisibility(View.INVISIBLE); 495 v.setRecycler(mCache); 496 } 497 498 mParam = getIntent().getParcelableExtra(KEY_IMAGE_LIST); 499 500 if (instanceState != null) { 501 mSavedUri = instanceState.getParcelable(STATE_URI); 502 mShowControls = instanceState.getBoolean(STATE_SHOW_CONTROLS, true); 503 } else { 504 mSavedUri = getIntent().getData(); 505 } 506 507 int[] pickIds = {R.id.attach, R.id.cancel}; 508 int[] reviewIds = {R.id.btn_delete, R.id.btn_share, R.id.btn_set_as, 509 R.id.btn_play, R.id.btn_done}; 510 int[] connectIds = isPickIntent() ? pickIds : reviewIds; 511 for (int id : connectIds) { 512 View view = mControlBar.findViewById(id); 513 view.setOnClickListener(this); 514 // Set the LinearLayout of the given button to visible 515 ((View) view.getParent()).setVisibility(View.VISIBLE); 516 } 517 518 if (mFullScreenInNormalMode) { 519 getWindow().addFlags( 520 WindowManager.LayoutParams.FLAG_FULLSCREEN); 521 } 522 523 setupOnScreenControls(findViewById(R.id.mainPanel), mImageView); 524 } 525 setButtonPanelVisibility(int id, int visibility)526 private void setButtonPanelVisibility(int id, int visibility) { 527 View button = mControlBar.findViewById(id); 528 ((View) button.getParent()).setVisibility(visibility); 529 } 530 updateActionIcons()531 private void updateActionIcons() { 532 if (isPickIntent()) return; 533 534 IImage image = mAllImages.getImageAt(mCurrentPosition); 535 if (image instanceof VideoObject) { 536 setButtonPanelVisibility(R.id.btn_set_as, View.GONE); 537 setButtonPanelVisibility(R.id.btn_play, View.VISIBLE); 538 } else { 539 setButtonPanelVisibility(R.id.btn_set_as, View.VISIBLE); 540 setButtonPanelVisibility(R.id.btn_play, View.GONE); 541 } 542 } 543 makeGetter()544 private void makeGetter() { 545 mGetter = new ImageGetter(getContentResolver()); 546 } 547 buildImageListFromUri(Uri uri)548 private IImageList buildImageListFromUri(Uri uri) { 549 int sort = ImageManager.SORT_ASCENDING; 550 return ImageManager.makeImageList(getContentResolver(), uri, sort); 551 } 552 init(Uri uri)553 private boolean init(Uri uri) { 554 if (uri == null) return false; 555 mAllImages = (mParam == null) 556 ? buildImageListFromUri(uri) 557 : ImageManager.makeImageList(getContentResolver(), mParam); 558 IImage image = mAllImages.getImageForUri(uri); 559 if (image == null) return false; 560 mCurrentPosition = mAllImages.getImageIndex(image); 561 return true; 562 } 563 getCurrentUri()564 private Uri getCurrentUri() { 565 if (mAllImages.getCount() == 0) return null; 566 IImage image = mAllImages.getImageAt(mCurrentPosition); 567 return image.fullSizeImageUri(); 568 } 569 570 @Override onSaveInstanceState(Bundle b)571 public void onSaveInstanceState(Bundle b) { 572 super.onSaveInstanceState(b); 573 b.putParcelable(STATE_URI, 574 mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri()); 575 b.putBoolean(STATE_SHOW_CONTROLS, mShowControls); 576 } 577 578 @Override onStart()579 public void onStart() { 580 super.onStart(); 581 mPaused = false; 582 583 if (!init(mSavedUri)) { 584 Log.w(TAG, "init failed: " + mSavedUri); 585 finish(); 586 return; 587 } 588 589 // normally this will never be zero but if one "backs" into this 590 // activity after removing the sdcard it could be zero. in that 591 // case just "finish" since there's nothing useful that can happen. 592 int count = mAllImages.getCount(); 593 if (count == 0) { 594 finish(); 595 return; 596 } else if (count <= mCurrentPosition) { 597 mCurrentPosition = count - 1; 598 } 599 600 if (mGetter == null) { 601 makeGetter(); 602 } 603 604 //show controls only for first time 605 setImage(mCurrentPosition, mShowControls); 606 mShowControls = false; 607 } 608 609 @Override onStop()610 public void onStop() { 611 super.onStop(); 612 mPaused = true; 613 614 // mGetter could be null if we call finish() and leave early in 615 // onStart(). 616 if (mGetter != null) { 617 mGetter.cancelCurrent(); 618 mGetter.stop(); 619 mGetter = null; 620 } 621 622 // removing all callback in the message queue 623 mHandler.removeAllGetterCallbacks(); 624 625 if (mAllImages != null) { 626 mSavedUri = getCurrentUri(); 627 mAllImages.close(); 628 mAllImages = null; 629 } 630 631 hideOnScreenControls(); 632 mImageView.clear(); 633 mCache.clear(); 634 635 for (ImageViewTouchBase iv : mSlideShowImageViews) { 636 iv.clear(); 637 } 638 } 639 startShareMediaActivity(IImage image)640 private void startShareMediaActivity(IImage image) { 641 boolean isVideo = image instanceof VideoObject; 642 Intent intent = new Intent(); 643 intent.setAction(Intent.ACTION_SEND); 644 intent.setType(image.getMimeType()); 645 intent.putExtra(Intent.EXTRA_STREAM, image.fullSizeImageUri()); 646 try { 647 startActivity(Intent.createChooser(intent, getText( 648 isVideo ? R.string.sendVideo : R.string.sendImage))); 649 } catch (android.content.ActivityNotFoundException ex) { 650 Toast.makeText(this, isVideo 651 ? R.string.no_way_to_share_image 652 : R.string.no_way_to_share_video, 653 Toast.LENGTH_SHORT).show(); 654 } 655 } 656 startPlayVideoActivity()657 private void startPlayVideoActivity() { 658 IImage image = mAllImages.getImageAt(mCurrentPosition); 659 Intent intent = new Intent( 660 Intent.ACTION_VIEW, image.fullSizeImageUri()); 661 try { 662 startActivity(intent); 663 } catch (android.content.ActivityNotFoundException ex) { 664 Log.e(TAG, "Couldn't view video " + image.fullSizeImageUri(), ex); 665 } 666 } 667 onClick(View v)668 public void onClick(View v) { 669 switch (v.getId()) { 670 case R.id.btn_delete: 671 MenuHelper.deleteImage(this, mDeletePhotoRunnable, 672 mAllImages.getImageAt(mCurrentPosition)); 673 break; 674 case R.id.btn_play: 675 startPlayVideoActivity(); 676 break; 677 case R.id.btn_share: { 678 IImage image = mAllImages.getImageAt(mCurrentPosition); 679 if (!MenuHelper.isWhiteListUri(image.fullSizeImageUri())) { 680 return; 681 } 682 startShareMediaActivity(image); 683 break; 684 } 685 case R.id.btn_set_as: { 686 IImage image = mAllImages.getImageAt(mCurrentPosition); 687 Intent intent = Util.createSetAsIntent(image); 688 try { 689 startActivity(Intent.createChooser( 690 intent, getText(R.string.setImage))); 691 } catch (android.content.ActivityNotFoundException ex) { 692 Toast.makeText(this, R.string.no_way_to_share_video, 693 Toast.LENGTH_SHORT).show(); 694 } 695 break; 696 } 697 case R.id.btn_done: 698 finish(); 699 break; 700 case R.id.next_image: 701 moveNextOrPrevious(1); 702 break; 703 case R.id.prev_image: 704 moveNextOrPrevious(-1); 705 break; 706 } 707 } 708 moveNextOrPrevious(int delta)709 private void moveNextOrPrevious(int delta) { 710 int nextImagePos = mCurrentPosition + delta; 711 if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) { 712 setImage(nextImagePos, true); 713 showOnScreenControls(); 714 } 715 } 716 717 @Override onActivityResult(int requestCode, int resultCode, Intent data)718 protected void onActivityResult(int requestCode, int resultCode, 719 Intent data) { 720 switch (requestCode) { 721 case MenuHelper.RESULT_COMMON_MENU_CROP: 722 if (resultCode == RESULT_OK) { 723 // The CropImage activity passes back the Uri of the 724 // cropped image as the Action rather than the Data. 725 mSavedUri = Uri.parse(data.getAction()); 726 727 // if onStart() runs before, then set the returned 728 // image as currentImage. 729 if (mAllImages != null) { 730 IImage image = mAllImages.getImageForUri(mSavedUri); 731 // image could be null if SD card is removed. 732 if (image == null) { 733 finish(); 734 } else { 735 mCurrentPosition = mAllImages.getImageIndex(image); 736 setImage(mCurrentPosition, false); 737 } 738 } 739 } 740 break; 741 } 742 } 743 } 744 745 class ImageViewTouch2 extends ImageViewTouchBase { 746 private final ReviewImage mViewImage; 747 private boolean mEnableTrackballScroll; 748 749 public ImageViewTouch2(Context context) { 750 super(context); 751 mViewImage = (ReviewImage) context; 752 } 753 754 public ImageViewTouch2(Context context, AttributeSet attrs) { 755 super(context, attrs); 756 mViewImage = (ReviewImage) context; 757 } 758 759 public void setEnableTrackballScroll(boolean enable) { 760 mEnableTrackballScroll = enable; 761 } 762 763 protected void postTranslateCenter(float dx, float dy) { 764 super.postTranslate(dx, dy); 765 center(true, true); 766 } 767 768 private static final float PAN_RATE = 20; 769 770 // This is the time we allow the dpad to change the image position again. 771 private long mNextChangePositionTime; 772 773 @Override 774 public boolean onKeyDown(int keyCode, KeyEvent event) { 775 if (mViewImage.mPaused) return false; 776 777 // Don't respond to arrow keys if trackball scrolling is not enabled 778 if (!mEnableTrackballScroll) { 779 if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP) 780 && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) { 781 return super.onKeyDown(keyCode, event); 782 } 783 } 784 785 int current = mViewImage.mCurrentPosition; 786 787 int nextImagePos = -2; // default no next image 788 try { 789 switch (keyCode) { 790 case KeyEvent.KEYCODE_DPAD_CENTER: { 791 if (mViewImage.isPickIntent()) { 792 IImage img = mViewImage.mAllImages 793 .getImageAt(mViewImage.mCurrentPosition); 794 mViewImage.setResult(ReviewImage.RESULT_OK, 795 new Intent().setData(img.fullSizeImageUri())); 796 mViewImage.finish(); 797 } 798 break; 799 } 800 case KeyEvent.KEYCODE_DPAD_LEFT: { 801 if (getScale() <= 1F && event.getEventTime() 802 >= mNextChangePositionTime) { 803 nextImagePos = current - 1; 804 mNextChangePositionTime = event.getEventTime() + 500; 805 } else { 806 panBy(PAN_RATE, 0); 807 center(true, false); 808 } 809 return true; 810 } 811 case KeyEvent.KEYCODE_DPAD_RIGHT: { 812 if (getScale() <= 1F && event.getEventTime() 813 >= mNextChangePositionTime) { 814 nextImagePos = current + 1; 815 mNextChangePositionTime = event.getEventTime() + 500; 816 } else { 817 panBy(-PAN_RATE, 0); 818 center(true, false); 819 } 820 return true; 821 } 822 case KeyEvent.KEYCODE_DPAD_UP: { 823 panBy(0, PAN_RATE); 824 center(false, true); 825 return true; 826 } 827 case KeyEvent.KEYCODE_DPAD_DOWN: { 828 panBy(0, -PAN_RATE); 829 center(false, true); 830 return true; 831 } 832 case KeyEvent.KEYCODE_DEL: 833 MenuHelper.deletePhoto( 834 mViewImage, mViewImage.mDeletePhotoRunnable); 835 break; 836 } 837 } finally { 838 if (nextImagePos >= 0 839 && nextImagePos < mViewImage.mAllImages.getCount()) { 840 synchronized (mViewImage) { 841 mViewImage.setImage(nextImagePos, true); 842 } 843 } else if (nextImagePos != -2) { 844 center(true, true); 845 } 846 } 847 848 return super.onKeyDown(keyCode, event); 849 } 850 } 851