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