OSDN Git Service

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