• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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