blob: 711ef2c9efe2dbba6c61627585a484de6f1487fc [file] [log] [blame]
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +08001/*
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
17package com.android.camera;
18
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080019import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.SharedPreferences;
23import android.graphics.Bitmap;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080024import android.net.Uri;
25import android.os.Bundle;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080026import android.preference.PreferenceManager;
27import android.provider.MediaStore;
28import android.util.AttributeSet;
29import android.util.Log;
30import android.view.GestureDetector;
31import android.view.KeyEvent;
32import android.view.Menu;
33import android.view.MenuItem;
34import android.view.MotionEvent;
35import android.view.View;
36import android.view.Window;
37import android.view.WindowManager;
Owen Lin71ea3a32009-07-30 10:23:30 +080038import android.view.View.OnTouchListener;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080039import android.view.animation.AlphaAnimation;
40import android.view.animation.Animation;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080041import android.widget.Toast;
42import android.widget.ZoomButtonsController;
43
Owen Lin8b3ecb22009-09-02 16:25:08 +080044import com.android.camera.gallery.IImage;
45import com.android.camera.gallery.IImageList;
46import com.android.camera.gallery.VideoObject;
47
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080048// 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.
53public class ReviewImage extends Activity implements View.OnClickListener {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080054 private static final String STATE_URI = "uri";
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080055 private static final String TAG = "ReviewImage";
56
Chih-Chung Chang4250e212009-07-24 10:58:40 +080057 private ImageGetter mGetter;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080058 private Uri mSavedUri;
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +080059 private boolean mPaused = true;
Owen Lin69bd2712009-08-06 15:29:34 +080060 private boolean mShowControls = true;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080061
62 // Choices for what adjacents to load.
63 private static final int[] sOrderAdjacents = new int[] {0, 1, -1};
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080064
Chih-Chung Chang4250e212009-07-24 10:58:40 +080065 final GetterHandler mHandler = new GetterHandler();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080066
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080067 private boolean mFullScreenInNormalMode;
68
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080069 int mCurrentPosition = 0;
70
71 // represents which style animation to use
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080072
73 private SharedPreferences mPrefs;
74
75 private View mRootView;
76 private View mControlBar;
77 private View mNextImageView;
78 private View mPrevImageView;
Chih-Chung Chang91acfc92009-07-06 15:37:24 +080079 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);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080087
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080088 public static final String KEY_IMAGE_LIST = "image_list";
Owen Lin69bd2712009-08-06 15:29:34 +080089 private static final String STATE_SHOW_CONTROLS = "show_controls";
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080090
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +080091 ImageManager.ImageListParam mParam;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080092 IImageList mAllImages;
93
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +080094 private final ImageViewTouchBase [] mSlideShowImageViews =
95 new ImageViewTouchBase[2];
96
97 GestureDetector mGestureDetector;
98 private ZoomButtonsController mZoomButtonsController;
99
100 // The image view displayed for normal mode.
101 private ImageViewTouch2 mImageView;
102 // This is the cache for thumbnail bitmaps.
103 private BitmapCache mCache;
104 private MenuHelper.MenuItemsResult mImageMenuRunnable;
Owen Lin71ea3a32009-07-30 10:23:30 +0800105 private final Runnable mDismissOnScreenControlRunner = new Runnable() {
106 public void run() {
107 hideOnScreenControls();
108 }
109 };
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800110
111 private void updateNextPrevControls() {
112 boolean showPrev = mCurrentPosition > 0;
113 boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
114
115 boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
116 boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
117
118 if (showPrev && !prevIsVisible) {
119 Animation a = mShowPrevImageViewAnimation;
120 a.setDuration(500);
121 mPrevImageView.startAnimation(a);
122 mPrevImageView.setVisibility(View.VISIBLE);
123 } else if (!showPrev && prevIsVisible) {
124 Animation a = mHidePrevImageViewAnimation;
125 a.setDuration(500);
126 mPrevImageView.startAnimation(a);
127 mPrevImageView.setVisibility(View.GONE);
128 }
129
130 if (showNext && !nextIsVisible) {
131 Animation a = mShowNextImageViewAnimation;
132 a.setDuration(500);
133 mNextImageView.startAnimation(a);
134 mNextImageView.setVisibility(View.VISIBLE);
135 } else if (!showNext && nextIsVisible) {
136 Animation a = mHideNextImageViewAnimation;
137 a.setDuration(500);
138 mNextImageView.startAnimation(a);
139 mNextImageView.setVisibility(View.GONE);
140 }
141 }
142
Owen Lin71ea3a32009-07-30 10:23:30 +0800143 private void showOnScreenControls() {
144
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800145 // If the view has not been attached to the window yet, the
146 // zoomButtonControls will not able to show up. So delay it until the
147 // view has attached to window.
148 if (mRootView.getWindowToken() == null) {
149 mHandler.postGetterCallback(new Runnable() {
150 public void run() {
Owen Lin71ea3a32009-07-30 10:23:30 +0800151 showOnScreenControls();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800152 }
153 });
154 return;
155 }
Owen Lin71ea3a32009-07-30 10:23:30 +0800156
157 // we may need to update the next/prev button due to index changing
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800158 updateNextPrevControls();
159
Owen Lin71ea3a32009-07-30 10:23:30 +0800160 if (ImageManager.isImage(mAllImages.getImageAt(mCurrentPosition))) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800161 updateZoomButtonsEnabled();
162 mZoomButtonsController.setVisible(true);
Owen Lin71ea3a32009-07-30 10:23:30 +0800163 } else {
164 mZoomButtonsController.setVisible(false);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800165 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800166 }
167
Owen Lin71ea3a32009-07-30 10:23:30 +0800168 private void hideOnScreenControls() {
169 if (mNextImageView.getVisibility() == View.VISIBLE) {
170 Animation a = mHideNextImageViewAnimation;
171 a.setDuration(500);
172 mNextImageView.startAnimation(a);
173 mNextImageView.setVisibility(View.INVISIBLE);
174 }
175
176 if (mPrevImageView.getVisibility() == View.VISIBLE) {
177 Animation a = mHidePrevImageViewAnimation;
178 a.setDuration(500);
179 mPrevImageView.startAnimation(a);
180 mPrevImageView.setVisibility(View.INVISIBLE);
181 }
182
183 mZoomButtonsController.setVisible(false);
184 }
185
186 private void scheduleDismissOnScreenControls() {
187 mHandler.removeCallbacks(mDismissOnScreenControlRunner);
188 mHandler.postDelayed(mDismissOnScreenControlRunner, 2000);
Owen Lin0ac86072009-07-30 10:23:30 +0800189 }
190
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800191 @Override
192 public boolean dispatchTouchEvent(MotionEvent m) {
Owen Lin8b3ecb22009-09-02 16:25:08 +0800193 if (mZoomButtonsController.isVisible()) {
Owen Lin71ea3a32009-07-30 10:23:30 +0800194 scheduleDismissOnScreenControls();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800195 }
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800196 return super.dispatchTouchEvent(m);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800197 }
198
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800199 private void updateZoomButtonsEnabled() {
200 ImageViewTouch2 imageView = mImageView;
201 float scale = imageView.getScale();
202 mZoomButtonsController.setZoomInEnabled(scale < imageView.mMaxZoom);
203 mZoomButtonsController.setZoomOutEnabled(scale > 1);
204 }
205
206 @Override
207 protected void onDestroy() {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800208 // This is necessary to make the ZoomButtonsController unregister
209 // its configuration change receiver.
210 if (mZoomButtonsController != null) {
211 mZoomButtonsController.setVisible(false);
212 }
213
214 super.onDestroy();
215 }
216
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800217 private void setupOnScreenControls(View rootView, View ownerView) {
Owen Lin71ea3a32009-07-30 10:23:30 +0800218 mNextImageView = rootView.findViewById(R.id.next_image);
219 mPrevImageView = rootView.findViewById(R.id.prev_image);
220
221 mNextImageView.setOnClickListener(this);
222 mPrevImageView.setOnClickListener(this);
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800223
224 setupZoomButtonController(ownerView);
225 setupOnTouchListeners(rootView);
Owen Lin71ea3a32009-07-30 10:23:30 +0800226 }
227
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800228 private void setupZoomButtonController(final View ownerView) {
229 mZoomButtonsController = new ZoomButtonsController(ownerView);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800230 mZoomButtonsController.setAutoDismissed(false);
Wu-cheng Li559899a2009-06-30 15:26:48 +0800231 mZoomButtonsController.setZoomSpeed(100);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800232 mZoomButtonsController.setOnZoomListener(
233 new ZoomButtonsController.OnZoomListener() {
234 public void onVisibilityChanged(boolean visible) {
235 if (visible) {
236 updateZoomButtonsEnabled();
237 }
238 }
239
240 public void onZoom(boolean zoomIn) {
241 if (zoomIn) {
242 mImageView.zoomIn();
243 } else {
244 mImageView.zoomOut();
245 }
Owen Lin71ea3a32009-07-30 10:23:30 +0800246 mZoomButtonsController.setVisible(true);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800247 updateZoomButtonsEnabled();
248 }
249 });
250 }
251
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800252 private void setupOnTouchListeners(View rootView) {
253 mGestureDetector = new GestureDetector(this, new MyGestureListener());
254
255 // If the user touches anywhere on the panel (including the
256 // next/prev button). We show the on-screen controls. In addition
257 // to that, if the touch is not on the prev/next button, we
258 // pass the event to the gesture detector to detect double tap.
259 final OnTouchListener buttonListener = new OnTouchListener() {
260 public boolean onTouch(View v, MotionEvent event) {
261 scheduleDismissOnScreenControls();
262 return false;
263 }
264 };
265
266 OnTouchListener rootListener = new OnTouchListener() {
267 public boolean onTouch(View v, MotionEvent event) {
268 buttonListener.onTouch(v, event);
269 mGestureDetector.onTouchEvent(event);
270
271 // We do not use the return value of
272 // mGestureDetector.onTouchEvent because we will not receive
273 // the "up" event if we return false for the "down" event.
274 return true;
275 }
276 };
277
278 mNextImageView.setOnTouchListener(buttonListener);
279 mPrevImageView.setOnTouchListener(buttonListener);
280 rootView.setOnTouchListener(rootListener);
281 }
282
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800283 private class MyGestureListener extends
284 GestureDetector.SimpleOnGestureListener {
285
286 @Override
287 public boolean onScroll(MotionEvent e1, MotionEvent e2,
288 float distanceX, float distanceY) {
289 ImageViewTouch2 imageView = mImageView;
290 if (imageView.getScale() > 1F) {
291 imageView.postTranslateCenter(-distanceX, -distanceY);
292 }
293 return true;
294 }
295
296 @Override
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800297 public boolean onSingleTapConfirmed(MotionEvent e) {
298 showOnScreenControls();
299 scheduleDismissOnScreenControls();
300 return true;
301 }
302
303 @Override
304 public boolean onDoubleTap(MotionEvent e) {
305 ImageViewTouch2 imageView = mImageView;
306
307 // Switch between the original scale and 3x scale.
308 if (imageView.getScale() > 2F) {
309 mImageView.zoomTo(1f);
310 } else {
311 mImageView.zoomToPoint(3f, e.getX(), e.getY());
312 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800313 return true;
314 }
315 }
316
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800317 boolean isPickIntent() {
318 String action = getIntent().getAction();
319 return (Intent.ACTION_PICK.equals(action)
320 || Intent.ACTION_GET_CONTENT.equals(action));
321 }
322
323 @Override
324 public boolean onCreateOptionsMenu(Menu menu) {
325 super.onCreateOptionsMenu(menu);
326
Chih-Chung Changcd65ac72009-09-08 14:36:48 +0800327 // These are the menu items already covered by the side buttons.
328 int sideButtons = MenuHelper.INCLUDE_DELETE_MENU
329 | MenuHelper.INCLUDE_SHARE_MENU
330 | MenuHelper.INCLUDE_SET_MENU
331 | MenuHelper.INCLUDE_VIEWPLAY_MENU;
332
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800333 mImageMenuRunnable = MenuHelper.addImageMenuItems(
334 menu,
Chih-Chung Changcd65ac72009-09-08 14:36:48 +0800335 MenuHelper.INCLUDE_ALL & ~sideButtons,
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800336 ReviewImage.this,
337 mHandler,
338 mDeletePhotoRunnable,
339 new MenuHelper.MenuInvoker() {
340 public void run(final MenuHelper.MenuCallback cb) {
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800341 if (mPaused) return;
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800342 IImage image = mAllImages.getImageAt(mCurrentPosition);
343 Uri uri = image.fullSizeImageUri();
344 cb.run(uri, image);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800345
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800346 mImageView.clear();
Owen Lin69bd2712009-08-06 15:29:34 +0800347 setImage(mCurrentPosition, false);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800348 }
349 });
350
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800351 return true;
352 }
353
354 protected Runnable mDeletePhotoRunnable = new Runnable() {
355 public void run() {
356 mAllImages.removeImageAt(mCurrentPosition);
357 if (mAllImages.getCount() == 0) {
358 finish();
Owen Lin71ea3a32009-07-30 10:23:30 +0800359 return;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800360 } else {
361 if (mCurrentPosition == mAllImages.getCount()) {
362 mCurrentPosition -= 1;
363 }
364 }
365 mImageView.clear();
366 mCache.clear(); // Because the position number is changed.
Owen Lin69bd2712009-08-06 15:29:34 +0800367 setImage(mCurrentPosition, true);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800368 }
369 };
370
371 @Override
372 public boolean onPrepareOptionsMenu(Menu menu) {
373 super.onPrepareOptionsMenu(menu);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800374
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800375 IImage image = mAllImages.getImageAt(mCurrentPosition);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800376 if (mImageMenuRunnable != null) {
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800377 mImageMenuRunnable.gettingReadyToOpen(menu, image);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800378 }
379
380 Uri uri = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
Chih-Chung Changb2a97652009-07-10 18:39:49 +0800381 MenuHelper.enableShareMenuItem(menu, MenuHelper.isWhiteListUri(uri));
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800382
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800383 MenuHelper.enableShowOnMapMenuItem(menu, MenuHelper.hasLatLngData(image));
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800384
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800385 return true;
386 }
387
388 @Override
389 public boolean onMenuItemSelected(int featureId, MenuItem item) {
390 boolean b = super.onMenuItemSelected(featureId, item);
391 if (mImageMenuRunnable != null) {
392 mImageMenuRunnable.aboutToCall(item,
393 mAllImages.getImageAt(mCurrentPosition));
394 }
395 return b;
396 }
397
Owen Lin69bd2712009-08-06 15:29:34 +0800398 void setImage(int pos, boolean showControls) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800399 mCurrentPosition = pos;
400
401 Bitmap b = mCache.getBitmap(pos);
402 if (b != null) {
Ray Chen012d0f32009-07-20 16:33:41 +0800403 IImage image = mAllImages.getImageAt(pos);
404 mImageView.setImageRotateBitmapResetBase(
405 new RotateBitmap(b, image.getDegreesRotated()), true);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800406 updateZoomButtonsEnabled();
407 }
408
409 ImageGetterCallback cb = new ImageGetterCallback() {
Chih-Chung Chang4250e212009-07-24 10:58:40 +0800410 public void completed() {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800411 }
412
413 public boolean wantsThumbnail(int pos, int offset) {
414 return !mCache.hasBitmap(pos + offset);
415 }
416
417 public boolean wantsFullImage(int pos, int offset) {
418 return offset == 0;
419 }
420
421 public int fullImageSizeToUse(int pos, int offset) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800422 // this number should be bigger so that we can zoom. we may
423 // need to get fancier and read in the fuller size image as the
Wei-Ta Chend5d74642009-06-11 16:13:13 +0800424 // user starts to zoom.
425 // Originally the value is set to 480 in order to avoid OOM.
Wei-Ta Chenf2c7e982009-07-29 17:24:50 +0800426 // Now we set it to 2048 because of using
427 // native memory allocation for Bitmaps.
Wei-Ta Chend5d74642009-06-11 16:13:13 +0800428 final int imageViewSize = 2048;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800429 return imageViewSize;
430 }
431
432 public int [] loadOrder() {
433 return sOrderAdjacents;
434 }
435
Ray Chen012d0f32009-07-20 16:33:41 +0800436 public void imageLoaded(int pos, int offset, RotateBitmap bitmap,
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800437 boolean isThumb) {
438 // shouldn't get here after onPause()
439
440 // We may get a result from a previous request. Ignore it.
441 if (pos != mCurrentPosition) {
442 bitmap.recycle();
443 return;
444 }
445
446 if (isThumb) {
Ray Chen012d0f32009-07-20 16:33:41 +0800447 mCache.put(pos + offset, bitmap.getBitmap());
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800448 }
449 if (offset == 0) {
450 // isThumb: We always load thumb bitmap first, so we will
451 // reset the supp matrix for then thumb bitmap, and keep
452 // the supp matrix when the full bitmap is loaded.
Ray Chen012d0f32009-07-20 16:33:41 +0800453 mImageView.setImageRotateBitmapResetBase(bitmap, isThumb);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800454 updateZoomButtonsEnabled();
455 }
456 }
457 };
458
459 // Could be null if we're stopping a slide show in the course of pausing
460 if (mGetter != null) {
Chih-Chung Chang4250e212009-07-24 10:58:40 +0800461 mGetter.setPosition(pos, cb, mAllImages, mHandler);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800462 }
463 updateActionIcons();
Owen Lin69bd2712009-08-06 15:29:34 +0800464 if (showControls) showOnScreenControls();
Owen Lin71ea3a32009-07-30 10:23:30 +0800465 scheduleDismissOnScreenControls();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800466 }
467
468 @Override
469 public void onCreate(Bundle instanceState) {
470 super.onCreate(instanceState);
471
472 Intent intent = getIntent();
473 mFullScreenInNormalMode = intent.getBooleanExtra(
474 MediaStore.EXTRA_FULL_SCREEN, true);
475
476 mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
477
478 setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
479 requestWindowFeature(Window.FEATURE_NO_TITLE);
480 setContentView(R.layout.review_image);
481
482 mRootView = findViewById(R.id.root);
483 mControlBar = findViewById(R.id.control_bar);
484 mImageView = (ImageViewTouch2) findViewById(R.id.image);
485 mImageView.setEnableTrackballScroll(true);
486 mCache = new BitmapCache(3);
487 mImageView.setRecycler(mCache);
488
Owen Lin69bd2712009-08-06 15:29:34 +0800489
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800490 makeGetter();
491
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800492 mSlideShowImageViews[0] =
493 (ImageViewTouchBase) findViewById(R.id.image1_slideShow);
494 mSlideShowImageViews[1] =
495 (ImageViewTouchBase) findViewById(R.id.image2_slideShow);
496 for (ImageViewTouchBase v : mSlideShowImageViews) {
497 v.setVisibility(View.INVISIBLE);
498 v.setRecycler(mCache);
499 }
500
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800501 mParam = getIntent().getParcelableExtra(KEY_IMAGE_LIST);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800502
503 if (instanceState != null) {
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800504 mSavedUri = instanceState.getParcelable(STATE_URI);
Owen Lin69bd2712009-08-06 15:29:34 +0800505 mShowControls = instanceState.getBoolean(STATE_SHOW_CONTROLS, true);
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800506 } else {
507 mSavedUri = getIntent().getData();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800508 }
509
510 int[] pickIds = {R.id.attach, R.id.cancel};
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800511 int[] reviewIds = {R.id.btn_delete, R.id.btn_share, R.id.btn_set_as,
512 R.id.btn_play, R.id.btn_done};
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800513 int[] connectIds = isPickIntent() ? pickIds : reviewIds;
514 for (int id : connectIds) {
515 View view = mControlBar.findViewById(id);
516 view.setOnClickListener(this);
517 // Set the LinearLayout of the given button to visible
518 ((View) view.getParent()).setVisibility(View.VISIBLE);
519 }
520
Owen Lin71ea3a32009-07-30 10:23:30 +0800521 if (mFullScreenInNormalMode) {
522 getWindow().addFlags(
523 WindowManager.LayoutParams.FLAG_FULLSCREEN);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800524 }
525
Chih-Chung Changeb9b5372009-09-10 12:36:52 +0800526 setupOnScreenControls(findViewById(R.id.mainPanel), mImageView);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800527 }
528
529 private void setButtonPanelVisibility(int id, int visibility) {
530 View button = mControlBar.findViewById(id);
531 ((View) button.getParent()).setVisibility(visibility);
532 }
533
534 private void updateActionIcons() {
535 if (isPickIntent()) return;
536
537 IImage image = mAllImages.getImageAt(mCurrentPosition);
538 if (image instanceof VideoObject) {
539 setButtonPanelVisibility(R.id.btn_set_as, View.GONE);
540 setButtonPanelVisibility(R.id.btn_play, View.VISIBLE);
541 } else {
542 setButtonPanelVisibility(R.id.btn_set_as, View.VISIBLE);
543 setButtonPanelVisibility(R.id.btn_play, View.GONE);
544 }
545 }
546
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800547 private void makeGetter() {
Chih-Chung Chang4250e212009-07-24 10:58:40 +0800548 mGetter = new ImageGetter();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800549 }
550
551 private IImageList buildImageListFromUri(Uri uri) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800552 int sort = ImageManager.SORT_ASCENDING;
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800553 return ImageManager.makeImageList(getContentResolver(), uri, sort);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800554 }
555
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800556 private boolean init(Uri uri) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800557 if (uri == null) return false;
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800558 mAllImages = (mParam == null)
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800559 ? buildImageListFromUri(uri)
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800560 : ImageManager.makeImageList(getContentResolver(), mParam);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800561 IImage image = mAllImages.getImageForUri(uri);
562 if (image == null) return false;
563 mCurrentPosition = mAllImages.getImageIndex(image);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800564 return true;
565 }
566
567 private Uri getCurrentUri() {
Owen Lin387833a2009-06-29 17:30:24 -0700568 if (mAllImages.getCount() == 0) return null;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800569 IImage image = mAllImages.getImageAt(mCurrentPosition);
570 return image.fullSizeImageUri();
571 }
572
573 @Override
574 public void onSaveInstanceState(Bundle b) {
575 super.onSaveInstanceState(b);
576 b.putParcelable(STATE_URI,
577 mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri());
Owen Lin69bd2712009-08-06 15:29:34 +0800578 b.putBoolean(STATE_SHOW_CONTROLS, mShowControls);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800579 }
580
581 @Override
582 public void onStart() {
583 super.onStart();
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800584 mPaused = false;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800585
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800586 if (!init(mSavedUri)) {
587 finish();
588 return;
589 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800590
591 // normally this will never be zero but if one "backs" into this
592 // activity after removing the sdcard it could be zero. in that
593 // case just "finish" since there's nothing useful that can happen.
594 int count = mAllImages.getCount();
595 if (count == 0) {
596 finish();
Owen Lin71ea3a32009-07-30 10:23:30 +0800597 return;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800598 } else if (count <= mCurrentPosition) {
599 mCurrentPosition = count - 1;
600 }
601
602 if (mGetter == null) {
603 makeGetter();
604 }
605
Owen Lin69bd2712009-08-06 15:29:34 +0800606 //show controls only for first time
607 setImage(mCurrentPosition, mShowControls);
608 mShowControls = false;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800609 }
610
611 @Override
612 public void onStop() {
613 super.onStop();
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800614 mPaused = true;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800615
616 mGetter.cancelCurrent();
617 mGetter.stop();
618 mGetter = null;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800619
620 // removing all callback in the message queue
621 mHandler.removeAllGetterCallbacks();
622
Chih-Chung Changf5bf8ca2009-08-25 18:28:29 +0800623 if (mAllImages != null) {
624 mSavedUri = getCurrentUri();
625 mAllImages.close();
626 mAllImages = null;
627 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800628
Owen Lin71ea3a32009-07-30 10:23:30 +0800629 hideOnScreenControls();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800630 mImageView.clear();
631 mCache.clear();
632
633 for (ImageViewTouchBase iv : mSlideShowImageViews) {
634 iv.clear();
635 }
636 }
637
638 private void startShareMediaActivity(IImage image) {
639 boolean isVideo = image instanceof VideoObject;
640 Intent intent = new Intent();
641 intent.setAction(Intent.ACTION_SEND);
642 intent.setType(image.getMimeType());
643 intent.putExtra(Intent.EXTRA_STREAM, image.fullSizeImageUri());
644 try {
645 startActivity(Intent.createChooser(intent, getText(
646 isVideo ? R.string.sendVideo : R.string.sendImage)));
647 } catch (android.content.ActivityNotFoundException ex) {
648 Toast.makeText(this, isVideo
649 ? R.string.no_way_to_share_image
650 : R.string.no_way_to_share_video,
651 Toast.LENGTH_SHORT).show();
652 }
653 }
654
655 private void startPlayVideoActivity() {
656 IImage image = mAllImages.getImageAt(mCurrentPosition);
657 Intent intent = new Intent(
658 Intent.ACTION_VIEW, image.fullSizeImageUri());
659 try {
660 startActivity(intent);
661 } catch (android.content.ActivityNotFoundException ex) {
662 Log.e(TAG, "Couldn't view video " + image.fullSizeImageUri(), ex);
663 }
664 }
665
666 public void onClick(View v) {
667 switch (v.getId()) {
668 case R.id.btn_delete:
repo sync4adbd032009-06-25 10:56:45 +0800669 MenuHelper.deleteImage(this, mDeletePhotoRunnable,
670 mAllImages.getImageAt(mCurrentPosition));
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800671 break;
672 case R.id.btn_play:
673 startPlayVideoActivity();
674 break;
675 case R.id.btn_share: {
676 IImage image = mAllImages.getImageAt(mCurrentPosition);
Chih-Chung Changb2a97652009-07-10 18:39:49 +0800677 if (!MenuHelper.isWhiteListUri(image.fullSizeImageUri())) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800678 return;
679 }
680 startShareMediaActivity(image);
681 break;
682 }
683 case R.id.btn_set_as: {
Chih-Chung Changbb187782009-07-02 16:46:30 +0800684 IImage image = mAllImages.getImageAt(mCurrentPosition);
685 Intent intent = Util.createSetAsIntent(image);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800686 try {
687 startActivity(Intent.createChooser(
688 intent, getText(R.string.setImage)));
689 } catch (android.content.ActivityNotFoundException ex) {
690 Toast.makeText(this, R.string.no_way_to_share_video,
691 Toast.LENGTH_SHORT).show();
692 }
693 break;
694 }
695 case R.id.btn_done:
696 finish();
697 break;
698 case R.id.next_image:
699 moveNextOrPrevious(1);
700 break;
701 case R.id.prev_image:
702 moveNextOrPrevious(-1);
703 break;
704 }
705 }
706
707 private void moveNextOrPrevious(int delta) {
708 int nextImagePos = mCurrentPosition + delta;
709 if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) {
Owen Lin69bd2712009-08-06 15:29:34 +0800710 setImage(nextImagePos, true);
Owen Lin71ea3a32009-07-30 10:23:30 +0800711 showOnScreenControls();
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800712 }
713 }
714
715 @Override
716 protected void onActivityResult(int requestCode, int resultCode,
717 Intent data) {
718 switch (requestCode) {
719 case MenuHelper.RESULT_COMMON_MENU_CROP:
720 if (resultCode == RESULT_OK) {
721 // The CropImage activity passes back the Uri of the
722 // cropped image as the Action rather than the Data.
723 mSavedUri = Uri.parse(data.getAction());
724 }
725 break;
726 }
727 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800728}
729
730class ImageViewTouch2 extends ImageViewTouchBase {
731 private final ReviewImage mViewImage;
732 private boolean mEnableTrackballScroll;
733
734 public ImageViewTouch2(Context context) {
735 super(context);
736 mViewImage = (ReviewImage) context;
737 }
738
739 public ImageViewTouch2(Context context, AttributeSet attrs) {
740 super(context, attrs);
741 mViewImage = (ReviewImage) context;
742 }
743
744 public void setEnableTrackballScroll(boolean enable) {
745 mEnableTrackballScroll = enable;
746 }
747
748 protected void postTranslateCenter(float dx, float dy) {
749 super.postTranslate(dx, dy);
750 center(true, true);
751 }
752
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800753 private static final float PAN_RATE = 20;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800754
Chih-Chung Chang1f463a62009-07-30 17:59:09 +0800755 // This is the time we allow the dpad to change the image position again.
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800756 private long mNextChangePositionTime;
Chih-Chung Chang1f463a62009-07-30 17:59:09 +0800757
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800758 @Override
759 public boolean onKeyDown(int keyCode, KeyEvent event) {
760 // Don't respond to arrow keys if trackball scrolling is not enabled
761 if (!mEnableTrackballScroll) {
762 if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP)
763 && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) {
764 return super.onKeyDown(keyCode, event);
765 }
766 }
767
768 int current = mViewImage.mCurrentPosition;
769
770 int nextImagePos = -2; // default no next image
771 try {
772 switch (keyCode) {
773 case KeyEvent.KEYCODE_DPAD_CENTER: {
774 if (mViewImage.isPickIntent()) {
775 IImage img = mViewImage.mAllImages
776 .getImageAt(mViewImage.mCurrentPosition);
777 mViewImage.setResult(ReviewImage.RESULT_OK,
778 new Intent().setData(img.fullSizeImageUri()));
779 mViewImage.finish();
780 }
781 break;
782 }
783 case KeyEvent.KEYCODE_DPAD_LEFT: {
Chih-Chung Chang1f463a62009-07-30 17:59:09 +0800784 if (getScale() <= 1F && event.getEventTime()
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800785 >= mNextChangePositionTime) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800786 nextImagePos = current - 1;
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800787 mNextChangePositionTime = event.getEventTime() + 500;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800788 } else {
789 panBy(PAN_RATE, 0);
790 center(true, false);
791 }
792 return true;
793 }
794 case KeyEvent.KEYCODE_DPAD_RIGHT: {
Chih-Chung Chang1f463a62009-07-30 17:59:09 +0800795 if (getScale() <= 1F && event.getEventTime()
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800796 >= mNextChangePositionTime) {
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800797 nextImagePos = current + 1;
Chih-Chung Chang522e8362009-08-26 16:12:34 +0800798 mNextChangePositionTime = event.getEventTime() + 500;
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800799 } else {
800 panBy(-PAN_RATE, 0);
801 center(true, false);
802 }
803 return true;
804 }
805 case KeyEvent.KEYCODE_DPAD_UP: {
806 panBy(0, PAN_RATE);
807 center(false, true);
808 return true;
809 }
810 case KeyEvent.KEYCODE_DPAD_DOWN: {
811 panBy(0, -PAN_RATE);
812 center(false, true);
813 return true;
814 }
815 case KeyEvent.KEYCODE_DEL:
816 MenuHelper.deletePhoto(
817 mViewImage, mViewImage.mDeletePhotoRunnable);
818 break;
819 }
820 } finally {
821 if (nextImagePos >= 0
822 && nextImagePos < mViewImage.mAllImages.getCount()) {
823 synchronized (mViewImage) {
Owen Lin69bd2712009-08-06 15:29:34 +0800824 mViewImage.setImage(nextImagePos, true);
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800825 }
826 } else if (nextImagePos != -2) {
827 center(true, true);
828 }
829 }
830
831 return super.onKeyDown(keyCode, event);
832 }
Wu-cheng Li46fc7ae2009-06-19 19:28:47 +0800833}