PhotoPage.java revision b1e9fd893d62adae1f92d29dfc08bc76f8764491
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
17package com.android.gallery3d.app;
18
19import android.app.ActionBar.OnMenuVisibilityListener;
20import android.app.Activity;
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.Intent;
24import android.graphics.Rect;
25import android.net.Uri;
26import android.nfc.NfcAdapter;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.Message;
30import android.view.Menu;
31import android.view.MenuInflater;
32import android.view.MenuItem;
33import android.view.View;
34import android.view.WindowManager;
35import android.widget.ShareActionProvider;
36import android.widget.Toast;
37
38import com.android.gallery3d.R;
39import com.android.gallery3d.data.DataManager;
40import com.android.gallery3d.data.MediaDetails;
41import com.android.gallery3d.data.MediaItem;
42import com.android.gallery3d.data.MediaObject;
43import com.android.gallery3d.data.MediaSet;
44import com.android.gallery3d.data.MtpDevice;
45import com.android.gallery3d.data.Path;
46import com.android.gallery3d.data.SnailItem;
47import com.android.gallery3d.data.SnailSource;
48import com.android.gallery3d.picasasource.PicasaSource;
49import com.android.gallery3d.ui.DetailsHelper;
50import com.android.gallery3d.ui.DetailsHelper.CloseListener;
51import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
52import com.android.gallery3d.ui.GLCanvas;
53import com.android.gallery3d.ui.GLView;
54import com.android.gallery3d.ui.ImportCompleteListener;
55import com.android.gallery3d.ui.MenuExecutor;
56import com.android.gallery3d.ui.PhotoView;
57import com.android.gallery3d.ui.ScreenNail;
58import com.android.gallery3d.ui.SelectionManager;
59import com.android.gallery3d.ui.SynchronizedHandler;
60import com.android.gallery3d.ui.UserInteractionListener;
61import com.android.gallery3d.util.GalleryUtils;
62
63public class PhotoPage extends ActivityState implements
64        PhotoView.Listener, UserInteractionListener,
65        OrientationManager.Listener, AppBridge.Server {
66    private static final String TAG = "PhotoPage";
67
68    private static final int MSG_HIDE_BARS = 1;
69    private static final int MSG_LOCK_ORIENTATION = 2;
70    private static final int MSG_UNLOCK_ORIENTATION = 3;
71    private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
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    public static final String KEY_APP_BRIDGE = "app-bridge";
84
85    public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
86
87    private GalleryApp mApplication;
88    private SelectionManager mSelectionManager;
89
90    private PhotoView mPhotoView;
91    private PhotoPage.Model mModel;
92    private DetailsHelper mDetailsHelper;
93    private boolean mShowDetails;
94    private Path mPendingSharePath;
95
96    // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
97    // E.g., viewing a photo in gmail attachment
98    private MediaSet mMediaSet;
99    private Menu mMenu;
100
101    private int mCurrentIndex = 0;
102    private Handler mHandler;
103    private boolean mShowBars = true;
104    private GalleryActionBar mActionBar;
105    private MyMenuVisibilityListener mMenuVisibilityListener;
106    private boolean mIsMenuVisible;
107    private boolean mIsInteracting;
108    private MediaItem mCurrentPhoto = null;
109    private MenuExecutor mMenuExecutor;
110    private boolean mIsActive;
111    private ShareActionProvider mShareActionProvider;
112    private String mSetPathString;
113    // This is the original mSetPathString before adding the camera preview item.
114    private String mOriginalSetPathString;
115    private AppBridge mAppBridge;
116    private ScreenNail mScreenNail;
117    private MediaItem mScreenNailItem;
118    private OrientationManager mOrientationManager;
119
120    private NfcAdapter mNfcAdapter;
121
122    public static interface Model extends PhotoView.Model {
123        public void resume();
124        public void pause();
125        public boolean isEmpty();
126        public MediaItem getCurrentMediaItem();
127        public void setCurrentPhoto(Path path, int indexHint);
128    }
129
130    private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
131        public void onMenuVisibilityChanged(boolean isVisible) {
132            mIsMenuVisible = isVisible;
133            refreshHidingMessage();
134        }
135    }
136
137    private final GLView mRootPane = new GLView() {
138
139        @Override
140        protected void renderBackground(GLCanvas view) {
141            view.clearBuffer();
142        }
143
144        @Override
145        protected void onLayout(
146                boolean changed, int left, int top, int right, int bottom) {
147            mPhotoView.layout(0, 0, right - left, bottom - top);
148            if (mShowDetails) {
149                mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom);
150            }
151        }
152
153        @Override
154        protected void orient(int displayRotation, int compensation) {
155            displayRotation = mOrientationManager.getDisplayRotation();
156            Log.d(TAG, "orient -- display rotation " + displayRotation
157                    + ", compensation = " + compensation);
158            super.orient(displayRotation, compensation);
159        }
160    };
161
162    @Override
163    public void onCreate(Bundle data, Bundle restoreState) {
164        mActionBar = mActivity.getGalleryActionBar();
165        mSelectionManager = new SelectionManager(mActivity, false);
166        mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager);
167
168        mPhotoView = new PhotoView(mActivity);
169        mPhotoView.setListener(this);
170        mRootPane.addComponent(mPhotoView);
171        mApplication = (GalleryApp)((Activity) mActivity).getApplication();
172        mOrientationManager = mActivity.getOrientationManager();
173        mOrientationManager.addListener(this);
174
175        mSetPathString = data.getString(KEY_MEDIA_SET_PATH);
176        mOriginalSetPathString = mSetPathString;
177        mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity.getAndroidContext());
178        Path itemPath = Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH));
179
180        if (mSetPathString != null) {
181            mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
182            if (mAppBridge != null) {
183                mOrientationManager.lockOrientation();
184
185                // Get the ScreenNail from AppBridge and register it.
186                mScreenNail = mAppBridge.attachScreenNail();
187                int id = SnailSource.registerScreenNail(mScreenNail);
188                Path screenNailSetPath = SnailSource.getSetPath(id);
189                Path screenNailItemPath = SnailSource.getItemPath(id);
190                mScreenNailItem = (MediaItem) mActivity.getDataManager()
191                        .getMediaObject(screenNailItemPath);
192
193                // Combine the original MediaSet with the one for CameraScreenNail.
194                mSetPathString = "/combo/item/{" + screenNailSetPath +
195                        "," + mSetPathString + "}";
196
197                // Start from the screen nail.
198                itemPath = screenNailItemPath;
199
200                // Action bar should not be displayed when camera starts.
201                mFlags |= FLAG_HIDE_ACTION_BAR;
202            }
203
204            mMediaSet = mActivity.getDataManager().getMediaSet(mSetPathString);
205            mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
206            if (mMediaSet == null) {
207                Log.w(TAG, "failed to restore " + mSetPathString);
208            }
209            PhotoDataAdapter pda = new PhotoDataAdapter(
210                    mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex,
211                    mAppBridge == null ? -1 : 0);
212            mModel = pda;
213            mPhotoView.setModel(mModel);
214
215            pda.setDataListener(new PhotoDataAdapter.DataListener() {
216
217                @Override
218                public void onPhotoChanged(int index, Path item) {
219                    mCurrentIndex = index;
220                    if (item != null) {
221                        MediaItem photo = mModel.getCurrentMediaItem();
222                        if (photo != null) updateCurrentPhoto(photo);
223                    }
224                }
225
226                @Override
227                public void onLoadingFinished() {
228                    GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
229                    if (!mModel.isEmpty()) {
230                        MediaItem photo = mModel.getCurrentMediaItem();
231                        if (photo != null) updateCurrentPhoto(photo);
232                    } else if (mIsActive) {
233                        mActivity.getStateManager().finishState(PhotoPage.this);
234                    }
235                }
236
237                @Override
238                public void onLoadingStarted() {
239                    GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
240                }
241            });
242        } else {
243            // Get default media set by the URI
244            MediaItem mediaItem = (MediaItem)
245                    mActivity.getDataManager().getMediaObject(itemPath);
246            mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem);
247            mPhotoView.setModel(mModel);
248            updateCurrentPhoto(mediaItem);
249        }
250
251        mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
252            @Override
253            public void handleMessage(Message message) {
254                switch (message.what) {
255                    case MSG_HIDE_BARS: {
256                        hideBars();
257                        break;
258                    }
259                    case MSG_LOCK_ORIENTATION: {
260                        mOrientationManager.lockOrientation();
261                        break;
262                    }
263                    case MSG_UNLOCK_ORIENTATION: {
264                        mOrientationManager.unlockOrientation();
265                        break;
266                    }
267                    case MSG_ON_FULL_SCREEN_CHANGED: {
268                        mAppBridge.onFullScreenChanged(message.arg1 == 1);
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    private void updateShareURI(Path path) {
283        if (mShareActionProvider != null) {
284            DataManager manager = mActivity.getDataManager();
285            int type = manager.getMediaType(path);
286            Intent intent = new Intent(Intent.ACTION_SEND);
287            intent.setType(MenuExecutor.getMimeType(type));
288            intent.putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path));
289            mShareActionProvider.setShareIntent(intent);
290            if (mNfcAdapter != null) {
291                mNfcAdapter.setBeamPushUris(new Uri[]{manager.getContentUri(path)},
292                        (Activity)mActivity);
293            }
294            mPendingSharePath = null;
295        } else {
296            // This happens when ActionBar is not created yet.
297            mPendingSharePath = path;
298        }
299    }
300
301    private void updateCurrentPhoto(MediaItem photo) {
302        if (mCurrentPhoto == photo) return;
303        mCurrentPhoto = photo;
304        if (mCurrentPhoto == null) return;
305        updateMenuOperations();
306        // Hide the action bar when going back to camera preview.
307        if (photo == mScreenNailItem) hideBars();
308        updateTitle();
309        if (mShowDetails) {
310            mDetailsHelper.reloadDetails(mModel.getCurrentIndex());
311        }
312        mPhotoView.showVideoPlayIcon(
313                photo.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO);
314
315        if ((photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
316            updateShareURI(photo.getPath());
317        }
318    }
319
320    private void updateTitle() {
321        if (mCurrentPhoto == null) return;
322        boolean showTitle = mActivity.getAndroidContext().getResources().getBoolean(
323                R.bool.show_action_bar_title);
324        if (showTitle && mCurrentPhoto.getName() != null)
325            mActionBar.setTitle(mCurrentPhoto.getName());
326        else
327            mActionBar.setTitle("");
328    }
329
330    private void updateMenuOperations() {
331        if (mMenu == null) return;
332        MenuItem item = mMenu.findItem(R.id.action_slideshow);
333        if (item != null) {
334            item.setVisible(canDoSlideShow());
335        }
336        if (mCurrentPhoto == null) return;
337        int supportedOperations = mCurrentPhoto.getSupportedOperations();
338        if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) {
339            supportedOperations &= ~MediaObject.SUPPORT_EDIT;
340        }
341
342        MenuExecutor.updateMenuOperation(mMenu, supportedOperations);
343    }
344
345    private boolean canDoSlideShow() {
346        if (mMediaSet == null || mCurrentPhoto == null) {
347            return false;
348        }
349        if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
350            return false;
351        }
352        if (mMediaSet instanceof MtpDevice) {
353            return false;
354        }
355        return true;
356    }
357
358    private void showBars() {
359        if (mShowBars) return;
360        mShowBars = true;
361        mActionBar.show();
362        WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes();
363        params.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
364        ((Activity) mActivity).getWindow().setAttributes(params);
365    }
366
367    private void hideBars() {
368        if (!mShowBars) return;
369        mShowBars = false;
370        mActionBar.hide();
371        WindowManager.LayoutParams params = ((Activity) mActivity).getWindow().getAttributes();
372        params.systemUiVisibility = View. SYSTEM_UI_FLAG_LOW_PROFILE;
373        ((Activity) mActivity).getWindow().setAttributes(params);
374    }
375
376    private void refreshHidingMessage() {
377        mHandler.removeMessages(MSG_HIDE_BARS);
378        if (!mIsMenuVisible && !mIsInteracting) {
379            mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
380        }
381    }
382
383    @Override
384    public void onUserInteraction() {
385        showBars();
386        refreshHidingMessage();
387    }
388
389    public void onUserInteractionTap() {
390        if (mShowBars) {
391            hideBars();
392            mHandler.removeMessages(MSG_HIDE_BARS);
393        } else {
394            showBars();
395            refreshHidingMessage();
396        }
397    }
398
399    @Override
400    public void onUserInteractionBegin() {
401        showBars();
402        mIsInteracting = true;
403        refreshHidingMessage();
404    }
405
406    @Override
407    public void onUserInteractionEnd() {
408        mIsInteracting = false;
409
410        // This function could be called from GL thread (in SlotView.render)
411        // and post to the main thread. So, it could be executed while the
412        // activity is paused.
413        if (mIsActive) refreshHidingMessage();
414    }
415
416    @Override
417    public void onOrientationCompensationChanged(int degrees) {
418        mActivity.getGLRoot().setOrientationCompensation(degrees);
419    }
420
421    @Override
422    protected void onBackPressed() {
423        if (mShowDetails) {
424            hideDetails();
425        } else if (mScreenNail == null
426                || !switchWithCaptureAnimation(-1)) {
427            // We are leaving this page. Set the result now.
428            setResult();
429            super.onBackPressed();
430        }
431    }
432
433    private void onUpPressed() {
434        if (mActivity.getStateManager().getStateCount() > 1) {
435            super.onBackPressed();
436        } else if (mOriginalSetPathString != null) {
437            // We're in view mode so set up the stacks on our own.
438            Bundle data = new Bundle(getData());
439            data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
440            data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
441                    mActivity.getDataManager().getTopSetPath(
442                            DataManager.INCLUDE_ALL));
443            mActivity.getStateManager().switchState(this, AlbumPage.class, data);
444        }
445    }
446
447    private void setResult() {
448        Intent result = null;
449        if (!mPhotoView.getFilmMode()) {
450            result = new Intent();
451            result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex);
452        }
453        setStateResult(Activity.RESULT_OK, result);
454    }
455
456    //////////////////////////////////////////////////////////////////////////
457    //  AppBridge.Server interface
458    //////////////////////////////////////////////////////////////////////////
459
460    @Override
461    public void setCameraNaturalFrame(Rect frame) {
462        mPhotoView.setCameraNaturalFrame(frame);
463    }
464
465    @Override
466    public boolean switchWithCaptureAnimation(int offset) {
467        return mPhotoView.switchWithCaptureAnimation(offset);
468    }
469
470    @Override
471    protected boolean onCreateActionBar(Menu menu) {
472        MenuInflater inflater = ((Activity) mActivity).getMenuInflater();
473        inflater.inflate(R.menu.photo, menu);
474        mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu);
475        if (mPendingSharePath != null) updateShareURI(mPendingSharePath);
476        mMenu = menu;
477        mShowBars = true;
478        updateMenuOperations();
479        updateTitle();
480        return true;
481    }
482
483    @Override
484    protected boolean onItemSelected(MenuItem item) {
485        MediaItem current = mModel.getCurrentMediaItem();
486
487        if (current == null) {
488            // item is not ready, ignore
489            return true;
490        }
491
492        int currentIndex = mModel.getCurrentIndex();
493        Path path = current.getPath();
494
495        DataManager manager = mActivity.getDataManager();
496        int action = item.getItemId();
497        boolean needsConfirm = false;
498        switch (action) {
499            case android.R.id.home: {
500                onUpPressed();
501                return true;
502            }
503            case R.id.action_slideshow: {
504                Bundle data = new Bundle();
505                data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString());
506                data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString());
507                data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex);
508                data.putBoolean(SlideshowPage.KEY_REPEAT, true);
509                mActivity.getStateManager().startStateForResult(
510                        SlideshowPage.class, REQUEST_SLIDESHOW, data);
511                return true;
512            }
513            case R.id.action_crop: {
514                Activity activity = (Activity) mActivity;
515                Intent intent = new Intent(CropImage.CROP_ACTION);
516                intent.setClass(activity, CropImage.class);
517                intent.setData(manager.getContentUri(path));
518                activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
519                        ? REQUEST_CROP_PICASA
520                        : REQUEST_CROP);
521                return true;
522            }
523            case R.id.action_details: {
524                if (mShowDetails) {
525                    hideDetails();
526                } else {
527                    showDetails(currentIndex);
528                }
529                return true;
530            }
531            case R.id.action_delete:
532                needsConfirm = true;
533            case R.id.action_setas:
534            case R.id.action_rotate_ccw:
535            case R.id.action_rotate_cw:
536            case R.id.action_show_on_map:
537            case R.id.action_edit:
538                mSelectionManager.deSelectAll();
539                mSelectionManager.toggle(path);
540                mMenuExecutor.onMenuClicked(item, needsConfirm, null);
541                return true;
542            case R.id.action_import:
543                mSelectionManager.deSelectAll();
544                mSelectionManager.toggle(path);
545                mMenuExecutor.onMenuClicked(item, needsConfirm,
546                        new ImportCompleteListener(mActivity));
547                return true;
548            default :
549                return false;
550        }
551    }
552
553    private void hideDetails() {
554        mShowDetails = false;
555        mDetailsHelper.hide();
556    }
557
558    private void showDetails(int index) {
559        mShowDetails = true;
560        if (mDetailsHelper == null) {
561            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
562            mDetailsHelper.setCloseListener(new CloseListener() {
563                public void onClose() {
564                    hideDetails();
565                }
566            });
567        }
568        mDetailsHelper.reloadDetails(index);
569        mDetailsHelper.show();
570    }
571
572    ////////////////////////////////////////////////////////////////////////////
573    //  Callbacks from PhotoView
574    ////////////////////////////////////////////////////////////////////////////
575    @Override
576    public void onSingleTapUp(int x, int y) {
577        if (mAppBridge != null) {
578            if (mAppBridge.onSingleTapUp(x, y)) return;
579        }
580
581        MediaItem item = mModel.getCurrentMediaItem();
582        if (item == null || item == mScreenNailItem) {
583            // item is not ready or it is camera preview, ignore
584            return;
585        }
586
587        boolean playVideo =
588                (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 0;
589
590        if (playVideo) {
591            // determine if the point is at center (1/6) of the photo view.
592            // (The position of the "play" icon is at center (1/6) of the photo)
593            int w = mPhotoView.getWidth();
594            int h = mPhotoView.getHeight();
595            playVideo = (Math.abs(x - w / 2) * 12 <= w)
596                && (Math.abs(y - h / 2) * 12 <= h);
597        }
598
599        if (playVideo) {
600            playVideo((Activity) mActivity, item.getPlayUri(), item.getName());
601        } else {
602            onUserInteractionTap();
603        }
604    }
605
606    @Override
607    public void lockOrientation() {
608        mHandler.sendEmptyMessage(MSG_LOCK_ORIENTATION);
609    }
610
611    @Override
612    public void unlockOrientation() {
613        // Temporarily disabled until Camera UI can switch orientation.
614        // mHandler.sendEmptyMessage(MSG_UNLOCK_ORIENTATION);
615    }
616
617    @Override
618    public void onFullScreenChanged(boolean full) {
619        Message m = mHandler.obtainMessage(
620                MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
621        m.sendToTarget();
622    }
623
624    public static void playVideo(Activity activity, Uri uri, String title) {
625        try {
626            Intent intent = new Intent(Intent.ACTION_VIEW)
627                    .setDataAndType(uri, "video/*");
628            intent.putExtra(Intent.EXTRA_TITLE, title);
629            activity.startActivity(intent);
630        } catch (ActivityNotFoundException e) {
631            Toast.makeText(activity, activity.getString(R.string.video_err),
632                    Toast.LENGTH_SHORT).show();
633        }
634    }
635
636    @Override
637    protected void onStateResult(int requestCode, int resultCode, Intent data) {
638        switch (requestCode) {
639            case REQUEST_CROP:
640                if (resultCode == Activity.RESULT_OK) {
641                    if (data == null) break;
642                    Path path = mApplication.getDataManager()
643                            .findPathByUri(data.getData(), data.getType());
644                    if (path != null) {
645                        mModel.setCurrentPhoto(path, mCurrentIndex);
646                    }
647                }
648                break;
649            case REQUEST_CROP_PICASA: {
650                Context context = mActivity.getAndroidContext();
651                // TODO: Use crop_saved instead of photo_saved after its new translation is done.
652                String message = resultCode == Activity.RESULT_OK ? context.getString(
653                        R.string.photo_saved, context.getString(R.string.folder_download))
654                        : context.getString(R.string.crop_not_saved);
655                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
656                break;
657            }
658            case REQUEST_SLIDESHOW: {
659                if (data == null) break;
660                String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH);
661                int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
662                if (path != null) {
663                    mModel.setCurrentPhoto(Path.fromString(path), index);
664                }
665            }
666        }
667    }
668
669    @Override
670    public void onPause() {
671        super.onPause();
672        mIsActive = false;
673        if (mAppBridge != null) mAppBridge.setServer(null);
674        DetailsHelper.pause();
675        mPhotoView.pause();
676        mModel.pause();
677        mHandler.removeMessages(MSG_HIDE_BARS);
678        mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
679
680        mMenuExecutor.pause();
681    }
682
683    @Override
684    protected void onResume() {
685        super.onResume();
686        mIsActive = true;
687        setContentPane(mRootPane);
688
689        mModel.resume();
690        mPhotoView.resume();
691        if (mMenuVisibilityListener == null) {
692            mMenuVisibilityListener = new MyMenuVisibilityListener();
693        }
694        mActionBar.setDisplayOptions(mSetPathString != null, true);
695        mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
696
697        onUserInteraction();
698        if (mAppBridge != null) {
699            mAppBridge.setServer(this);
700            mModel.moveTo(0);  // move to the camera preview after resume
701        }
702    }
703
704    @Override
705    protected void onDestroy() {
706        if (mAppBridge != null) {
707            // Unregister the ScreenNail and notify mAppBridge.
708            SnailSource.unregisterScreenNail(mScreenNail);
709            mAppBridge.detachScreenNail();
710            mAppBridge = null;
711            mScreenNail = null;
712        }
713        mOrientationManager.removeListener(this);
714        super.onDestroy();
715    }
716
717    private class MyDetailsSource implements DetailsSource {
718        private int mIndex;
719
720        @Override
721        public MediaDetails getDetails() {
722            return mModel.getCurrentMediaItem().getDetails();
723        }
724
725        @Override
726        public int size() {
727            return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
728        }
729
730        @Override
731        public int findIndex(int indexHint) {
732            mIndex = indexHint;
733            return indexHint;
734        }
735
736        @Override
737        public int getIndex() {
738            return mIndex;
739        }
740    }
741}
742