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