OSDN Git Service

Fix incorrect initial position/scale of opening animations.
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / app / PhotoPage.java
1 /*
2  * Copyright (C) 2010 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.gallery3d.app;
18
19 import android.app.ActionBar.OnMenuVisibilityListener;
20 import android.app.Activity;
21 import android.content.ActivityNotFoundException;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.graphics.Rect;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.view.Menu;
31 import android.view.MenuInflater;
32 import android.view.MenuItem;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.View.MeasureSpec;
36 import android.view.WindowManager;
37 import android.widget.ShareActionProvider;
38 import android.widget.Toast;
39
40 import com.android.gallery3d.R;
41 import com.android.gallery3d.data.DataManager;
42 import com.android.gallery3d.data.MediaDetails;
43 import com.android.gallery3d.data.MediaItem;
44 import com.android.gallery3d.data.MediaObject;
45 import com.android.gallery3d.data.MediaSet;
46 import com.android.gallery3d.data.MtpDevice;
47 import com.android.gallery3d.data.Path;
48 import com.android.gallery3d.data.SnailSource;
49 import com.android.gallery3d.picasasource.PicasaSource;
50 import com.android.gallery3d.ui.DetailsHelper;
51 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
52 import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
53 import com.android.gallery3d.ui.FilmStripView;
54 import com.android.gallery3d.ui.GLCanvas;
55 import com.android.gallery3d.ui.GLView;
56 import com.android.gallery3d.ui.ImportCompleteListener;
57 import com.android.gallery3d.ui.MenuExecutor;
58 import com.android.gallery3d.ui.PhotoView;
59 import com.android.gallery3d.ui.PositionRepository;
60 import com.android.gallery3d.ui.PositionRepository.Position;
61 import com.android.gallery3d.ui.SelectionManager;
62 import com.android.gallery3d.ui.SynchronizedHandler;
63 import com.android.gallery3d.ui.UserInteractionListener;
64 import com.android.gallery3d.util.GalleryUtils;
65
66 public class PhotoPage extends ActivityState
67         implements PhotoView.PhotoTapListener, FilmStripView.Listener,
68         UserInteractionListener {
69     private static final String TAG = "PhotoPage";
70
71     private static final int MSG_HIDE_BARS = 1;
72
73     private static final int HIDE_BARS_TIMEOUT = 3500;
74
75     private static final int REQUEST_SLIDESHOW = 1;
76     private static final int REQUEST_CROP = 2;
77     private static final int REQUEST_CROP_PICASA = 3;
78
79     public static final String KEY_MEDIA_SET_PATH = "media-set-path";
80     public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
81     public static final String KEY_INDEX_HINT = "index-hint";
82     public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
83
84     private GalleryApp mApplication;
85     private SelectionManager mSelectionManager;
86
87     private PhotoView mPhotoView;
88     private PhotoPage.Model mModel;
89     private FilmStripView mFilmStripView;
90     private DetailsHelper mDetailsHelper;
91     private boolean mShowDetails;
92     private Path mPendingSharePath;
93
94     // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
95     // E.g., viewing a photo in gmail attachment
96     private MediaSet mMediaSet;
97     private Menu mMenu;
98
99     private final Intent mResultIntent = new Intent();
100     private int mCurrentIndex = 0;
101     private Handler mHandler;
102     private boolean mShowBars = true;
103     private GalleryActionBar mActionBar;
104     private MyMenuVisibilityListener mMenuVisibilityListener;
105     private boolean mIsMenuVisible;
106     private boolean mIsInteracting;
107     private MediaItem mCurrentPhoto = null;
108     private MenuExecutor mMenuExecutor;
109     private boolean mIsActive;
110     private ShareActionProvider mShareActionProvider;
111     private String mSetPathString;
112
113     // This is for testing only. It should be removed once we have the real
114     // Camera view.
115     private CameraView mCameraView;
116     private ScreenNailBridge mScreenNail;
117
118     public static interface Model extends PhotoView.Model {
119         public void resume();
120         public void pause();
121         public boolean isEmpty();
122         public MediaItem getCurrentMediaItem();
123         public int getCurrentIndex();
124         public void setCurrentPhoto(Path path, int indexHint);
125     }
126
127     private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
128         public void onMenuVisibilityChanged(boolean isVisible) {
129             mIsMenuVisible = isVisible;
130             refreshHidingMessage();
131         }
132     }
133
134     private final GLView mRootPane = new GLView() {
135
136         @Override
137         protected void renderBackground(GLCanvas view) {
138             view.clearBuffer();
139         }
140
141         @Override
142         protected void onLayout(
143                 boolean changed, int left, int top, int right, int bottom) {
144             mPhotoView.layout(0, 0, right - left, bottom - top);
145             // Reset position offset after the layout is changed.
146             PositionRepository.getInstance(mActivity).setOffset(0, 0);
147             int filmStripHeight = 0;
148             if (mFilmStripView != null) {
149                 mFilmStripView.measure(
150                         MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY),
151                         MeasureSpec.UNSPECIFIED);
152                 filmStripHeight = mFilmStripView.getMeasuredHeight();
153                 mFilmStripView.layout(0, bottom - top - filmStripHeight,
154                         right - left, bottom - top);
155             }
156             if (mShowDetails) {
157                 mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom);
158             }
159         }
160     };
161
162     private void initFilmStripView() {
163         Config.PhotoPage config = Config.PhotoPage.get((Context) mActivity);
164         mFilmStripView = new FilmStripView(mActivity, mMediaSet,
165                 config.filmstripTopMargin, config.filmstripMidMargin, config.filmstripBottomMargin,
166                 config.filmstripContentSize, config.filmstripThumbSize, config.filmstripBarSize,
167                 config.filmstripGripSize, config.filmstripGripWidth);
168         mRootPane.addComponent(mFilmStripView);
169         mFilmStripView.setListener(this);
170         mFilmStripView.setUserInteractionListener(this);
171         mFilmStripView.setFocusIndex(mCurrentIndex);
172         mFilmStripView.setStartIndex(mCurrentIndex);
173         mRootPane.requestLayout();
174         if (mIsActive) mFilmStripView.resume();
175         if (!mShowBars) mFilmStripView.setVisibility(GLView.INVISIBLE);
176     }
177
178     @Override
179     public void onCreate(Bundle data, Bundle restoreState) {
180         mActionBar = mActivity.getGalleryActionBar();
181         mSelectionManager = new SelectionManager(mActivity, false);
182         mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager);
183
184         mPhotoView = new PhotoView(mActivity);
185         mPhotoView.setPhotoTapListener(this);
186         mRootPane.addComponent(mPhotoView);
187         mApplication = (GalleryApp)((Activity) mActivity).getApplication();
188
189         mSetPathString = data.getString(KEY_MEDIA_SET_PATH);
190         Path itemPath = Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH));
191
192         if (mSetPathString != null) {
193             // Uncomment the block below to test camera screennail.
194             /*
195             Path cameraScreenNailSetPath = addCameraScreenNail();
196
197             // Combine the original MediaSet with the one for camera ScreenNail.
198             mSetPathString = "/combo/item/{" + cameraScreenNailSetPath + "," +
199                     mSetPathString + "}";
200             */
201             mMediaSet = mActivity.getDataManager().getMediaSet(mSetPathString);
202             mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
203             mMediaSet = (MediaSet)
204                     mActivity.getDataManager().getMediaObject(mSetPathString);
205             if (mMediaSet == null) {
206                 Log.w(TAG, "failed to restore " + mSetPathString);
207             }
208             PhotoDataAdapter pda = new PhotoDataAdapter(
209                     mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex);
210             mModel = pda;
211             mPhotoView.setModel(mModel);
212
213             mResultIntent.putExtra(KEY_INDEX_HINT, mCurrentIndex);
214             setStateResult(Activity.RESULT_OK, mResultIntent);
215
216             pda.setDataListener(new PhotoDataAdapter.DataListener() {
217
218                 @Override
219                 public void onPhotoChanged(int index, Path item) {
220                     if (mFilmStripView != null) mFilmStripView.setFocusIndex(index);
221                     mCurrentIndex = index;
222                     mResultIntent.putExtra(KEY_INDEX_HINT, index);
223                     if (item != null) {
224                         mResultIntent.putExtra(KEY_MEDIA_ITEM_PATH, item.toString());
225                         MediaItem photo = mModel.getCurrentMediaItem();
226                         if (photo != null) updateCurrentPhoto(photo);
227                     } else {
228                         mResultIntent.removeExtra(KEY_MEDIA_ITEM_PATH);
229                     }
230                     setStateResult(Activity.RESULT_OK, mResultIntent);
231                 }
232
233                 @Override
234                 public void onLoadingFinished() {
235                     GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
236                     if (!mModel.isEmpty()) {
237                         MediaItem photo = mModel.getCurrentMediaItem();
238                         if (photo != null) updateCurrentPhoto(photo);
239                     } else if (mIsActive) {
240                         mActivity.getStateManager().finishState(PhotoPage.this);
241                     }
242                 }
243
244                 @Override
245                 public void onLoadingStarted() {
246                     GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
247                 }
248
249                 @Override
250                 public void onPhotoAvailable(long version, boolean fullImage) {
251                     if (mFilmStripView == null) initFilmStripView();
252                 }
253             });
254         } else {
255             // Get default media set by the URI
256             MediaItem mediaItem = (MediaItem)
257                     mActivity.getDataManager().getMediaObject(itemPath);
258             mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem);
259             mPhotoView.setModel(mModel);
260             updateCurrentPhoto(mediaItem);
261         }
262
263         mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
264             @Override
265             public void handleMessage(Message message) {
266                 switch (message.what) {
267                     case MSG_HIDE_BARS: {
268                         hideBars();
269                         break;
270                     }
271                     default: throw new AssertionError(message.what);
272                 }
273             }
274         };
275
276         // start the opening animation only if it's not restored.
277         if (restoreState == null) {
278             mPhotoView.setOpenAnimationRect((Rect) data.getParcelable(KEY_OPEN_ANIMATION_RECT));
279         }
280     }
281
282     // We create a Camera View and a ScreenNail. The two work together
283     // to present the view together with other pictures. Returns the
284     // Path of the MediaItem hosting the ScreenNail.
285     private Path addCameraScreenNail() {
286         // Create a camera view and add it to the root.
287         Activity activity = (Activity) mActivity;
288         mCameraView = new CameraView(activity);
289         ViewGroup galleryRoot = (ViewGroup) activity.findViewById(R.id.gallery_root);
290         galleryRoot.addView(mCameraView);
291
292         // Create a ScreenNail and register it.
293         mScreenNail = new ScreenNailBridge(mCameraView);
294         mCameraView.setScreenNailBridge(mScreenNail);
295         return SnailSource.registerScreenNail(mScreenNail);
296     }
297
298     private void removeCameraScreenNail() {
299         if (mCameraView == null) return;
300
301         // Remove the camera view.
302         ((ViewGroup) mCameraView.getParent()).removeView(mCameraView);
303         mCameraView = null;
304
305         // Unregister the ScreenNail.
306         SnailSource.unregisterScreenNail(mScreenNail);
307         mScreenNail = null;
308     }
309
310     private void updateShareURI(Path path) {
311         if (mShareActionProvider != null) {
312             DataManager manager = mActivity.getDataManager();
313             int type = manager.getMediaType(path);
314             Intent intent = new Intent(Intent.ACTION_SEND);
315             intent.setType(MenuExecutor.getMimeType(type));
316             intent.putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path));
317             mShareActionProvider.setShareIntent(intent);
318             mPendingSharePath = null;
319         } else {
320             // This happens when ActionBar is not created yet.
321             mPendingSharePath = path;
322         }
323     }
324
325     private void setTitle(String title) {
326         if (title == null) return;
327         boolean showTitle = mActivity.getAndroidContext().getResources().getBoolean(
328                 R.bool.show_action_bar_title);
329         if (showTitle)
330             mActionBar.setTitle(title);
331         else
332             mActionBar.setTitle("");
333     }
334
335     private void updateCurrentPhoto(MediaItem photo) {
336         if (mCurrentPhoto == photo) return;
337         mCurrentPhoto = photo;
338         if (mCurrentPhoto == null) return;
339         updateMenuOperations();
340         if (mShowDetails) {
341             mDetailsHelper.reloadDetails(mModel.getCurrentIndex());
342         }
343         setTitle(photo.getName());
344         mPhotoView.showVideoPlayIcon(
345                 photo.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO);
346
347         if ((photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
348             updateShareURI(photo.getPath());
349         }
350     }
351
352     private void updateMenuOperations() {
353         if (mMenu == null) return;
354         MenuItem item = mMenu.findItem(R.id.action_slideshow);
355         if (item != null) {
356             item.setVisible(canDoSlideShow());
357         }
358         if (mCurrentPhoto == null) return;
359         int supportedOperations = mCurrentPhoto.getSupportedOperations();
360         if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) {
361             supportedOperations &= ~MediaObject.SUPPORT_EDIT;
362         }
363
364         MenuExecutor.updateMenuOperation(mMenu, supportedOperations);
365     }
366
367     private boolean canDoSlideShow() {
368         if (mMediaSet == null || mCurrentPhoto == null) {
369             return false;
370         }
371         if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
372             return false;
373         }
374         if (mMediaSet instanceof MtpDevice) {
375             return false;
376         }
377         return true;
378     }
379
380     private void showBars() {
381         if (mShowBars) return;
382         mShowBars = true;
383         mActionBar.show();
384         WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes();
385         params.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
386         ((Activity) mActivity).getWindow().setAttributes(params);
387         if (mFilmStripView != null) {
388             mFilmStripView.show();
389         }
390     }
391
392     private void hideBars() {
393         if (!mShowBars) return;
394         mShowBars = false;
395         mActionBar.hide();
396         WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes();
397         params.systemUiVisibility = View. SYSTEM_UI_FLAG_LOW_PROFILE;
398         ((Activity) mActivity).getWindow().setAttributes(params);
399         if (mFilmStripView != null) {
400             mFilmStripView.hide();
401         }
402     }
403
404     private void refreshHidingMessage() {
405         mHandler.removeMessages(MSG_HIDE_BARS);
406         if (!mIsMenuVisible && !mIsInteracting) {
407             mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
408         }
409     }
410
411     @Override
412     public void onUserInteraction() {
413         showBars();
414         refreshHidingMessage();
415     }
416
417     public void onUserInteractionTap() {
418         if (mShowBars) {
419             hideBars();
420             mHandler.removeMessages(MSG_HIDE_BARS);
421         } else {
422             showBars();
423             refreshHidingMessage();
424         }
425     }
426
427     @Override
428     public void onUserInteractionBegin() {
429         showBars();
430         mIsInteracting = true;
431         refreshHidingMessage();
432     }
433
434     @Override
435     public void onUserInteractionEnd() {
436         mIsInteracting = false;
437
438         // This function could be called from GL thread (in SlotView.render)
439         // and post to the main thread. So, it could be executed while the
440         // activity is paused.
441         if (mIsActive) refreshHidingMessage();
442     }
443
444     @Override
445     protected void onBackPressed() {
446         if (mShowDetails) {
447             hideDetails();
448         } else {
449             PositionRepository repository = PositionRepository.getInstance(mActivity);
450             repository.clear();
451             if (mCurrentPhoto != null) {
452                 Position position = new Position();
453                 position.x = mRootPane.getWidth() / 2;
454                 position.y = mRootPane.getHeight() / 2;
455                 position.z = -1000;
456                 repository.putPosition(
457                         System.identityHashCode(mCurrentPhoto.getPath()),
458                         position);
459             }
460             super.onBackPressed();
461         }
462     }
463
464     @Override
465     protected boolean onCreateActionBar(Menu menu) {
466         MenuInflater inflater = ((Activity) mActivity).getMenuInflater();
467         inflater.inflate(R.menu.photo, menu);
468         mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu);
469         if (mPendingSharePath != null) updateShareURI(mPendingSharePath);
470         mMenu = menu;
471         mShowBars = true;
472         updateMenuOperations();
473         return true;
474     }
475
476     @Override
477     protected boolean onItemSelected(MenuItem item) {
478         MediaItem current = mModel.getCurrentMediaItem();
479
480         if (current == null) {
481             // item is not ready, ignore
482             return true;
483         }
484
485         int currentIndex = mModel.getCurrentIndex();
486         Path path = current.getPath();
487
488         DataManager manager = mActivity.getDataManager();
489         int action = item.getItemId();
490         switch (action) {
491             case android.R.id.home: {
492                 if (mSetPathString != null) {
493                     if (mActivity.getStateManager().getStateCount() > 1) {
494                         onBackPressed();
495                     } else {
496                         Activity a = (Activity) mActivity;
497                         Uri uri = mActivity.getDataManager().getContentUri(
498                                 Path.fromString(mSetPathString));
499                         Intent intent = new Intent(Intent.ACTION_VIEW)
500                                 .setClass(a, Gallery.class)
501                                 .setDataAndType(uri, ContentResolver.CURSOR_DIR_BASE_TYPE)
502                                 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
503                                         Intent.FLAG_ACTIVITY_NEW_TASK);
504                         a.startActivity(intent);
505                     }
506                 }
507                 return true;
508             }
509             case R.id.action_slideshow: {
510                 Bundle data = new Bundle();
511                 data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString());
512                 data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString());
513                 data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex);
514                 data.putBoolean(SlideshowPage.KEY_REPEAT, true);
515                 mActivity.getStateManager().startStateForResult(
516                         SlideshowPage.class, REQUEST_SLIDESHOW, data);
517                 return true;
518             }
519             case R.id.action_crop: {
520                 Activity activity = (Activity) mActivity;
521                 Intent intent = new Intent(CropImage.CROP_ACTION);
522                 intent.setClass(activity, CropImage.class);
523                 intent.setData(manager.getContentUri(path));
524                 activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
525                         ? REQUEST_CROP_PICASA
526                         : REQUEST_CROP);
527                 return true;
528             }
529             case R.id.action_details: {
530                 if (mShowDetails) {
531                     hideDetails();
532                 } else {
533                     showDetails(currentIndex);
534                 }
535                 return true;
536             }
537             case R.id.action_setas:
538             case R.id.action_confirm_delete:
539             case R.id.action_rotate_ccw:
540             case R.id.action_rotate_cw:
541             case R.id.action_show_on_map:
542             case R.id.action_edit:
543                 mSelectionManager.deSelectAll();
544                 mSelectionManager.toggle(path);
545                 mMenuExecutor.onMenuClicked(item, null);
546                 return true;
547             case R.id.action_import:
548                 mSelectionManager.deSelectAll();
549                 mSelectionManager.toggle(path);
550                 mMenuExecutor.onMenuClicked(item,
551                         new ImportCompleteListener(mActivity));
552                 return true;
553             default :
554                 return false;
555         }
556     }
557
558     private void hideDetails() {
559         mShowDetails = false;
560         mDetailsHelper.hide();
561     }
562
563     private void showDetails(int index) {
564         mShowDetails = true;
565         if (mDetailsHelper == null) {
566             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
567             mDetailsHelper.setCloseListener(new CloseListener() {
568                 public void onClose() {
569                     hideDetails();
570                 }
571             });
572         }
573         mDetailsHelper.reloadDetails(index);
574         mDetailsHelper.show();
575     }
576
577     public void onSingleTapUp(int x, int y) {
578         MediaItem item = mModel.getCurrentMediaItem();
579         if (item == null) {
580             // item is not ready, ignore
581             return;
582         }
583
584         boolean playVideo =
585                 (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 0;
586
587         if (playVideo) {
588             // determine if the point is at center (1/6) of the photo view.
589             // (The position of the "play" icon is at center (1/6) of the photo)
590             int w = mPhotoView.getWidth();
591             int h = mPhotoView.getHeight();
592             playVideo = (Math.abs(x - w / 2) * 12 <= w)
593                 && (Math.abs(y - h / 2) * 12 <= h);
594         }
595
596         if (playVideo) {
597             playVideo((Activity) mActivity, item.getPlayUri(), item.getName());
598         } else {
599             onUserInteractionTap();
600         }
601     }
602
603     public static void playVideo(Activity activity, Uri uri, String title) {
604         try {
605             Intent intent = new Intent(Intent.ACTION_VIEW)
606                     .setDataAndType(uri, "video/*");
607             intent.putExtra(Intent.EXTRA_TITLE, title);
608             activity.startActivity(intent);
609         } catch (ActivityNotFoundException e) {
610             Toast.makeText(activity, activity.getString(R.string.video_err),
611                     Toast.LENGTH_SHORT).show();
612         }
613     }
614
615     // Called by FileStripView.
616     // Returns false if it cannot jump to the specified index at this time.
617     public boolean onSlotSelected(int slotIndex) {
618         return mPhotoView.jumpTo(slotIndex);
619     }
620
621     @Override
622     protected void onStateResult(int requestCode, int resultCode, Intent data) {
623         switch (requestCode) {
624             case REQUEST_CROP:
625                 if (resultCode == Activity.RESULT_OK) {
626                     if (data == null) break;
627                     Path path = mApplication
628                             .getDataManager().findPathByUri(data.getData());
629                     if (path != null) {
630                         mModel.setCurrentPhoto(path, mCurrentIndex);
631                     }
632                 }
633                 break;
634             case REQUEST_CROP_PICASA: {
635                 int message = resultCode == Activity.RESULT_OK
636                         ? R.string.crop_saved
637                         : R.string.crop_not_saved;
638                 Toast.makeText(mActivity.getAndroidContext(),
639                         message, Toast.LENGTH_SHORT).show();
640                 break;
641             }
642             case REQUEST_SLIDESHOW: {
643                 if (data == null) break;
644                 String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH);
645                 int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
646                 if (path != null) {
647                     mModel.setCurrentPhoto(Path.fromString(path), index);
648                 }
649             }
650         }
651     }
652
653     @Override
654     public void onPause() {
655         super.onPause();
656         mIsActive = false;
657         if (mFilmStripView != null) {
658             mFilmStripView.pause();
659         }
660         DetailsHelper.pause();
661         mPhotoView.pause();
662         mModel.pause();
663         mHandler.removeMessages(MSG_HIDE_BARS);
664         mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
665         mMenuExecutor.pause();
666     }
667
668     @Override
669     protected void onResume() {
670         super.onResume();
671         mIsActive = true;
672         setContentPane(mRootPane);
673         // Reset position offset for resuming.
674         PositionRepository.getInstance(mActivity).setOffset(
675                 mPhotoView.bounds().left, mPhotoView.bounds().top);
676
677         mModel.resume();
678         mPhotoView.resume();
679         if (mFilmStripView != null) {
680             mFilmStripView.resume();
681         }
682         if (mMenuVisibilityListener == null) {
683             mMenuVisibilityListener = new MyMenuVisibilityListener();
684         }
685         mActionBar.setDisplayOptions(mSetPathString != null, true);
686         mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
687         onUserInteraction();
688     }
689
690     @Override
691     protected void onDestroy() {
692         removeCameraScreenNail();
693         super.onDestroy();
694     }
695
696     private class MyDetailsSource implements DetailsSource {
697         private int mIndex;
698
699         @Override
700         public MediaDetails getDetails() {
701             return mModel.getCurrentMediaItem().getDetails();
702         }
703
704         @Override
705         public int size() {
706             return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
707         }
708
709         @Override
710         public int findIndex(int indexHint) {
711             mIndex = indexHint;
712             return indexHint;
713         }
714
715         @Override
716         public int getIndex() {
717             return mIndex;
718         }
719     }
720 }