PhotoPage.java revision 61f94714c3702115d2f89bb5f8829697be0c3680
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.SnailSource;
47import com.android.gallery3d.picasasource.PicasaSource;
48import com.android.gallery3d.ui.DetailsHelper;
49import com.android.gallery3d.ui.DetailsHelper.CloseListener;
50import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
51import com.android.gallery3d.ui.GLCanvas;
52import com.android.gallery3d.ui.GLView;
53import com.android.gallery3d.ui.ImportCompleteListener;
54import com.android.gallery3d.ui.MenuExecutor;
55import com.android.gallery3d.ui.PhotoView;
56import com.android.gallery3d.ui.ScreenNail;
57import com.android.gallery3d.ui.SelectionManager;
58import com.android.gallery3d.ui.SynchronizedHandler;
59import com.android.gallery3d.util.GalleryUtils;
60
61public class PhotoPage extends ActivityState implements
62        PhotoView.Listener, OrientationManager.Listener, AppBridge.Server {
63    private static final String TAG = "PhotoPage";
64
65    private static final int MSG_HIDE_BARS = 1;
66    private static final int MSG_LOCK_ORIENTATION = 2;
67    private static final int MSG_UNLOCK_ORIENTATION = 3;
68    private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
69    private static final int MSG_UPDATE_ACTION_BAR = 5;
70
71    private static final int HIDE_BARS_TIMEOUT = 3500;
72
73    private static final int REQUEST_SLIDESHOW = 1;
74    private static final int REQUEST_CROP = 2;
75    private static final int REQUEST_CROP_PICASA = 3;
76    private static final int REQUEST_EDIT = 4;
77
78    public static final String KEY_MEDIA_SET_PATH = "media-set-path";
79    public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
80    public static final String KEY_INDEX_HINT = "index-hint";
81    public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
82    public static final String KEY_APP_BRIDGE = "app-bridge";
83
84    public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
85
86    private GalleryApp mApplication;
87    private SelectionManager mSelectionManager;
88
89    private PhotoView mPhotoView;
90    private PhotoPage.Model mModel;
91    private DetailsHelper mDetailsHelper;
92    private boolean mShowDetails;
93    private Path mPendingSharePath;
94
95    // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
96    // E.g., viewing a photo in gmail attachment
97    private MediaSet mMediaSet;
98    private Menu mMenu;
99
100    private int mCurrentIndex = 0;
101    private Handler mHandler;
102    private boolean mShowBars = true;
103    private volatile boolean mActionBarAllowed = true;
104    private GalleryActionBar mActionBar;
105    private MyMenuVisibilityListener mMenuVisibilityListener;
106    private boolean mIsMenuVisible;
107    private MediaItem mCurrentPhoto = null;
108    private MenuExecutor mMenuExecutor;
109    private boolean mIsActive;
110    private ShareActionProvider mShareActionProvider;
111    private String mSetPathString;
112    // This is the original mSetPathString before adding the camera preview item.
113    private String mOriginalSetPathString;
114    private AppBridge mAppBridge;
115    private ScreenNail mScreenNail;
116    private MediaItem mScreenNailItem;
117    private OrientationManager mOrientationManager;
118
119    private NfcAdapter mNfcAdapter;
120
121    public static interface Model extends PhotoView.Model {
122        public void resume();
123        public void pause();
124        public boolean isEmpty();
125        public MediaItem getCurrentMediaItem();
126        public void setCurrentPhoto(Path path, int indexHint);
127    }
128
129    private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
130        @Override
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                mShowBars = false;
203            }
204
205            mMediaSet = mActivity.getDataManager().getMediaSet(mSetPathString);
206            mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
207            if (mMediaSet == null) {
208                Log.w(TAG, "failed to restore " + mSetPathString);
209            }
210            PhotoDataAdapter pda = new PhotoDataAdapter(
211                    mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex,
212                    mAppBridge == null ? -1 : 0);
213            mModel = pda;
214            mPhotoView.setModel(mModel);
215
216            pda.setDataListener(new PhotoDataAdapter.DataListener() {
217
218                @Override
219                public void onPhotoChanged(int index, Path item) {
220                    mCurrentIndex = index;
221                    if (item != null) {
222                        MediaItem photo = mModel.getCurrentMediaItem();
223                        if (photo != null) updateCurrentPhoto(photo);
224                    }
225                    updateBars();
226                }
227
228                @Override
229                public void onLoadingFinished() {
230                    GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
231                    if (!mModel.isEmpty()) {
232                        MediaItem photo = mModel.getCurrentMediaItem();
233                        if (photo != null) updateCurrentPhoto(photo);
234                    } else if (mIsActive) {
235                        mActivity.getStateManager().finishState(PhotoPage.this);
236                    }
237                }
238
239                @Override
240                public void onLoadingStarted() {
241                    GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
242                }
243            });
244        } else {
245            // Get default media set by the URI
246            MediaItem mediaItem = (MediaItem)
247                    mActivity.getDataManager().getMediaObject(itemPath);
248            mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem);
249            mPhotoView.setModel(mModel);
250            updateCurrentPhoto(mediaItem);
251        }
252
253        mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
254            @Override
255            public void handleMessage(Message message) {
256                switch (message.what) {
257                    case MSG_HIDE_BARS: {
258                        hideBars();
259                        break;
260                    }
261                    case MSG_LOCK_ORIENTATION: {
262                        mOrientationManager.lockOrientation();
263                        break;
264                    }
265                    case MSG_UNLOCK_ORIENTATION: {
266                        mOrientationManager.unlockOrientation();
267                        break;
268                    }
269                    case MSG_ON_FULL_SCREEN_CHANGED: {
270                        mAppBridge.onFullScreenChanged(message.arg1 == 1);
271                        break;
272                    }
273                    case MSG_UPDATE_ACTION_BAR: {
274                        updateBars();
275                        break;
276                    }
277                    default: throw new AssertionError(message.what);
278                }
279            }
280        };
281
282        // start the opening animation only if it's not restored.
283        if (restoreState == null) {
284            mPhotoView.setOpenAnimationRect((Rect) data.getParcelable(KEY_OPEN_ANIMATION_RECT));
285        }
286    }
287
288    private void updateShareURI(Path path) {
289        if (mShareActionProvider != null) {
290            DataManager manager = mActivity.getDataManager();
291            int type = manager.getMediaType(path);
292            Intent intent = new Intent(Intent.ACTION_SEND);
293            intent.setType(MenuExecutor.getMimeType(type));
294            intent.putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path));
295            mShareActionProvider.setShareIntent(intent);
296            if (mNfcAdapter != null) {
297                mNfcAdapter.setBeamPushUris(new Uri[]{manager.getContentUri(path)},
298                        (Activity)mActivity);
299            }
300            mPendingSharePath = null;
301        } else {
302            // This happens when ActionBar is not created yet.
303            mPendingSharePath = path;
304        }
305    }
306
307    private void updateCurrentPhoto(MediaItem photo) {
308        if (mCurrentPhoto == photo) return;
309        mCurrentPhoto = photo;
310        if (mCurrentPhoto == null) return;
311        updateMenuOperations();
312        updateTitle();
313        if (mShowDetails) {
314            mDetailsHelper.reloadDetails(mModel.getCurrentIndex());
315        }
316        mPhotoView.showVideoPlayIcon(
317                photo.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO);
318
319        if ((photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
320            updateShareURI(photo.getPath());
321        }
322    }
323
324    private void updateTitle() {
325        if (mCurrentPhoto == null) return;
326        boolean showTitle = mActivity.getAndroidContext().getResources().getBoolean(
327                R.bool.show_action_bar_title);
328        if (showTitle && mCurrentPhoto.getName() != null)
329            mActionBar.setTitle(mCurrentPhoto.getName());
330        else
331            mActionBar.setTitle("");
332    }
333
334    private void updateMenuOperations() {
335        if (mMenu == null) return;
336        MenuItem item = mMenu.findItem(R.id.action_slideshow);
337        if (item != null) {
338            item.setVisible(canDoSlideShow());
339        }
340        if (mCurrentPhoto == null) return;
341        int supportedOperations = mCurrentPhoto.getSupportedOperations();
342        if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) {
343            supportedOperations &= ~MediaObject.SUPPORT_EDIT;
344        }
345
346        MenuExecutor.updateMenuOperation(mMenu, supportedOperations);
347    }
348
349    private boolean canDoSlideShow() {
350        if (mMediaSet == null || mCurrentPhoto == null) {
351            return false;
352        }
353        if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
354            return false;
355        }
356        if (mMediaSet instanceof MtpDevice) {
357            return false;
358        }
359        return true;
360    }
361
362    //////////////////////////////////////////////////////////////////////////
363    //  Action Bar show/hide management
364    //////////////////////////////////////////////////////////////////////////
365
366    private void showBars() {
367        if (mShowBars) return;
368        mShowBars = true;
369        mActionBar.show();
370        WindowManager.LayoutParams params =
371                ((Activity) mActivity).getWindow().getAttributes();
372        params.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
373        ((Activity) mActivity).getWindow().setAttributes(params);
374        refreshHidingMessage();
375    }
376
377    private void hideBars() {
378        if (!mShowBars) return;
379        mShowBars = false;
380        mActionBar.hide();
381        WindowManager.LayoutParams params =
382                ((Activity) mActivity).getWindow().getAttributes();
383        params.systemUiVisibility = View. SYSTEM_UI_FLAG_LOW_PROFILE;
384        ((Activity) mActivity).getWindow().setAttributes(params);
385        mHandler.removeMessages(MSG_HIDE_BARS);
386    }
387
388    private void refreshHidingMessage() {
389        mHandler.removeMessages(MSG_HIDE_BARS);
390        if (!mIsMenuVisible) {
391            mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
392        }
393    }
394
395    private void toggleBars() {
396        if (mShowBars) {
397            hideBars();
398        } else if (canShowBars()) {
399            showBars();
400        }
401    }
402
403    private void updateBars() {
404        if (canShowBars()) {
405            showBars();
406        } else {
407            hideBars();
408        }
409    }
410
411    private boolean canShowBars() {
412        boolean atCamera = mAppBridge != null && mCurrentIndex == 0;
413        return mActionBarAllowed && !atCamera;
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        updateMenuOperations();
478        updateTitle();
479        return true;
480    }
481
482    @Override
483    protected boolean onItemSelected(MenuItem item) {
484        MediaItem current = mModel.getCurrentMediaItem();
485
486        if (current == null) {
487            // item is not ready, ignore
488            return true;
489        }
490
491        int currentIndex = mModel.getCurrentIndex();
492        Path path = current.getPath();
493
494        DataManager manager = mActivity.getDataManager();
495        int action = item.getItemId();
496        boolean needsConfirm = false;
497        switch (action) {
498            case android.R.id.home: {
499                onUpPressed();
500                return true;
501            }
502            case R.id.action_slideshow: {
503                Bundle data = new Bundle();
504                data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString());
505                data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString());
506                data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex);
507                data.putBoolean(SlideshowPage.KEY_REPEAT, true);
508                mActivity.getStateManager().startStateForResult(
509                        SlideshowPage.class, REQUEST_SLIDESHOW, data);
510                return true;
511            }
512            case R.id.action_crop: {
513                Activity activity = (Activity) mActivity;
514                Intent intent = new Intent(CropImage.CROP_ACTION);
515                intent.setClass(activity, CropImage.class);
516                intent.setData(manager.getContentUri(path));
517                activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
518                        ? REQUEST_CROP_PICASA
519                        : REQUEST_CROP);
520                return true;
521            }
522            case R.id.action_edit: {
523                Intent intent = new Intent(Intent.ACTION_EDIT)
524                        .setData(manager.getContentUri(path))
525                        .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
526                ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null),
527                        REQUEST_EDIT);
528                return true;
529            }
530            case R.id.action_details: {
531                if (mShowDetails) {
532                    hideDetails();
533                } else {
534                    showDetails(currentIndex);
535                }
536                return true;
537            }
538            case R.id.action_delete:
539                needsConfirm = true;
540            case R.id.action_setas:
541            case R.id.action_rotate_ccw:
542            case R.id.action_rotate_cw:
543            case R.id.action_show_on_map:
544                mSelectionManager.deSelectAll();
545                mSelectionManager.toggle(path);
546                mMenuExecutor.onMenuClicked(item, needsConfirm, null);
547                return true;
548            case R.id.action_import:
549                mSelectionManager.deSelectAll();
550                mSelectionManager.toggle(path);
551                mMenuExecutor.onMenuClicked(item, needsConfirm,
552                        new ImportCompleteListener(mActivity));
553                return true;
554            default :
555                return false;
556        }
557    }
558
559    private void hideDetails() {
560        mShowDetails = false;
561        mDetailsHelper.hide();
562    }
563
564    private void showDetails(int index) {
565        mShowDetails = true;
566        if (mDetailsHelper == null) {
567            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
568            mDetailsHelper.setCloseListener(new CloseListener() {
569                @Override
570                public void onClose() {
571                    hideDetails();
572                }
573            });
574        }
575        mDetailsHelper.reloadDetails(index);
576        mDetailsHelper.show();
577    }
578
579    ////////////////////////////////////////////////////////////////////////////
580    //  Callbacks from PhotoView
581    ////////////////////////////////////////////////////////////////////////////
582    @Override
583    public void onSingleTapUp(int x, int y) {
584        if (mAppBridge != null) {
585            if (mAppBridge.onSingleTapUp(x, y)) return;
586        }
587
588        MediaItem item = mModel.getCurrentMediaItem();
589        if (item == null || item == mScreenNailItem) {
590            // item is not ready or it is camera preview, ignore
591            return;
592        }
593
594        boolean playVideo =
595                (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 0;
596
597        if (playVideo) {
598            // determine if the point is at center (1/6) of the photo view.
599            // (The position of the "play" icon is at center (1/6) of the photo)
600            int w = mPhotoView.getWidth();
601            int h = mPhotoView.getHeight();
602            playVideo = (Math.abs(x - w / 2) * 12 <= w)
603                && (Math.abs(y - h / 2) * 12 <= h);
604        }
605
606        if (playVideo) {
607            playVideo((Activity) mActivity, item.getPlayUri(), item.getName());
608        } else {
609            toggleBars();
610        }
611    }
612
613    @Override
614    public void lockOrientation() {
615        mHandler.sendEmptyMessage(MSG_LOCK_ORIENTATION);
616    }
617
618    @Override
619    public void unlockOrientation() {
620        mHandler.sendEmptyMessage(MSG_UNLOCK_ORIENTATION);
621    }
622
623    @Override
624    public void onActionBarAllowed(boolean allowed) {
625        mActionBarAllowed = allowed;
626        mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR);
627    }
628
629    @Override
630    public void onFullScreenChanged(boolean full) {
631        Message m = mHandler.obtainMessage(
632                MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
633        m.sendToTarget();
634    }
635
636    public static void playVideo(Activity activity, Uri uri, String title) {
637        try {
638            Intent intent = new Intent(Intent.ACTION_VIEW)
639                    .setDataAndType(uri, "video/*");
640            intent.putExtra(Intent.EXTRA_TITLE, title);
641            activity.startActivity(intent);
642        } catch (ActivityNotFoundException e) {
643            Toast.makeText(activity, activity.getString(R.string.video_err),
644                    Toast.LENGTH_SHORT).show();
645        }
646    }
647
648    private void setCurrentPhotoByIntent(Intent intent) {
649        if (intent == null) return;
650        Path path = mApplication.getDataManager()
651                .findPathByUri(intent.getData(), intent.getType());
652        if (path != null) {
653            mModel.setCurrentPhoto(path, mCurrentIndex);
654        }
655    }
656
657    @Override
658    protected void onStateResult(int requestCode, int resultCode, Intent data) {
659        switch (requestCode) {
660            case REQUEST_EDIT:
661                setCurrentPhotoByIntent(data);
662                break;
663            case REQUEST_CROP:
664                if (resultCode == Activity.RESULT_OK) {
665                    setCurrentPhotoByIntent(data);
666                }
667                break;
668            case REQUEST_CROP_PICASA: {
669                if (resultCode == Activity.RESULT_OK) {
670                    Context context = mActivity.getAndroidContext();
671                    // TODO: Use crop_saved instead of photo_saved after its new translation is done.
672                    String message = context.getString(R.string.photo_saved,
673                            context.getString(R.string.folder_download));
674                    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
675                }
676                break;
677            }
678            case REQUEST_SLIDESHOW: {
679                if (data == null) break;
680                String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH);
681                int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
682                if (path != null) {
683                    mModel.setCurrentPhoto(Path.fromString(path), index);
684                }
685            }
686        }
687    }
688
689    @Override
690    public void onPause() {
691        super.onPause();
692        mIsActive = false;
693        if (mAppBridge != null) mAppBridge.setServer(null);
694        DetailsHelper.pause();
695        mPhotoView.pause();
696        mModel.pause();
697        mHandler.removeMessages(MSG_HIDE_BARS);
698        mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
699
700        mMenuExecutor.pause();
701    }
702
703    @Override
704    protected void onResume() {
705        super.onResume();
706        mIsActive = true;
707        setContentPane(mRootPane);
708
709        mModel.resume();
710        mPhotoView.resume();
711        if (mMenuVisibilityListener == null) {
712            mMenuVisibilityListener = new MyMenuVisibilityListener();
713        }
714        mActionBar.setDisplayOptions(mSetPathString != null, true);
715        mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
716
717        if (mAppBridge != null) {
718            mAppBridge.setServer(this);
719            mPhotoView.resetToFirstPicture();
720        }
721    }
722
723    @Override
724    protected void onDestroy() {
725        if (mAppBridge != null) {
726            // Unregister the ScreenNail and notify mAppBridge.
727            SnailSource.unregisterScreenNail(mScreenNail);
728            mAppBridge.detachScreenNail();
729            mAppBridge = null;
730            mScreenNail = null;
731        }
732        mOrientationManager.removeListener(this);
733        super.onDestroy();
734    }
735
736    private class MyDetailsSource implements DetailsSource {
737        private int mIndex;
738
739        @Override
740        public MediaDetails getDetails() {
741            return mModel.getCurrentMediaItem().getDetails();
742        }
743
744        @Override
745        public int size() {
746            return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
747        }
748
749        @Override
750        public int findIndex(int indexHint) {
751            mIndex = indexHint;
752            return indexHint;
753        }
754
755        @Override
756        public int getIndex() {
757            return mIndex;
758        }
759    }
760}
761