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