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