2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.app;
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;
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;
68 public class PhotoPage extends ActivityState implements
69 PhotoView.Listener, OrientationManager.Listener, AppBridge.Server {
70 private static final String TAG = "PhotoPage";
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;
80 private static final int HIDE_BARS_TIMEOUT = 3500;
81 private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
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;
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";
95 public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
97 private GalleryApp mApplication;
98 private SelectionManager mSelectionManager;
100 private PhotoView mPhotoView;
101 private PhotoPage.Model mModel;
102 private DetailsHelper mDetailsHelper;
103 private boolean mShowDetails;
104 private Path mPendingSharePath;
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;
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;
131 private NfcAdapter mNfcAdapter;
133 public static interface Model extends PhotoView.Model {
134 public void resume();
136 public boolean isEmpty();
137 public void setCurrentPhoto(Path path, int indexHint);
140 private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
142 public void onMenuVisibilityChanged(boolean isVisible) {
143 mIsMenuVisible = isVisible;
144 refreshHidingMessage();
148 private final GLView mRootPane = new GLView() {
151 protected void renderBackground(GLCanvas view) {
156 protected void onLayout(
157 boolean changed, int left, int top, int right, int bottom) {
158 mPhotoView.layout(0, 0, right - left, bottom - top);
160 mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom);
166 public void onCreate(Bundle data, Bundle restoreState) {
167 mActionBar = mActivity.getGalleryActionBar();
168 mSelectionManager = new SelectionManager(mActivity, false);
169 mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager);
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);
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));
184 if (mSetPathString != null) {
185 mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
186 if (mAppBridge != null) {
187 mAppBridge.setServer(this);
188 mOrientationManager.lockOrientation();
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());
200 // Combine the original MediaSet with the one for ScreenNail
202 mSetPathString = "/combo/item/{" + screenNailSetPath +
203 "," + mSetPathString + "}";
205 // Start from the screen nail.
206 itemPath = screenNailItemPath;
208 // Action bar should not be displayed when camera starts.
209 mFlags |= FLAG_HIDE_ACTION_BAR | FLAG_HIDE_STATUS_BAR;
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);
219 PhotoDataAdapter pda = new PhotoDataAdapter(
220 mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex,
221 mAppBridge == null ? -1 : 0,
222 mAppBridge == null ? false : mAppBridge.isPanorama());
224 mPhotoView.setModel(mModel);
226 pda.setDataListener(new PhotoDataAdapter.DataListener() {
229 public void onPhotoChanged(int index, Path item) {
230 mCurrentIndex = index;
232 MediaItem photo = mModel.getMediaItem(0);
233 if (photo != null) updateCurrentPhoto(photo);
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);
249 public void onLoadingStarted() {
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);
261 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
263 public void handleMessage(Message message) {
264 switch (message.what) {
265 case MSG_HIDE_BARS: {
269 case MSG_LOCK_ORIENTATION: {
270 mOrientationManager.lockOrientation();
273 case MSG_UNLOCK_ORIENTATION: {
274 mOrientationManager.unlockOrientation();
277 case MSG_ON_FULL_SCREEN_CHANGED: {
278 mAppBridge.onFullScreenChanged(message.arg1 == 1);
281 case MSG_UPDATE_ACTION_BAR: {
285 case MSG_WANT_BARS: {
289 case MSG_UNFREEZE_GLROOT: {
290 mActivity.getGLRoot().unfreeze();
293 default: throw new AssertionError(message.what);
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));
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);
316 mPendingSharePath = null;
318 // This happens when ActionBar is not created yet.
319 mPendingSharePath = path;
323 private void updateCurrentPhoto(MediaItem photo) {
324 if (mCurrentPhoto == photo) return;
325 mCurrentPhoto = photo;
326 if (mCurrentPhoto == null) return;
327 updateMenuOperations();
330 mDetailsHelper.reloadDetails(mModel.getCurrentIndex());
332 if ((photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
333 updateShareURI(photo.getPath());
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());
344 mActionBar.setTitle("");
347 private void updateMenuOperations() {
348 if (mMenu == null) return;
349 MenuItem item = mMenu.findItem(R.id.action_slideshow);
351 item.setVisible(canDoSlideShow());
353 if (mCurrentPhoto == null) return;
354 int supportedOperations = mCurrentPhoto.getSupportedOperations();
355 if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) {
356 supportedOperations &= ~MediaObject.SUPPORT_EDIT;
359 MenuExecutor.updateMenuOperation(mMenu, supportedOperations);
362 private boolean canDoSlideShow() {
363 if (mMediaSet == null || mCurrentPhoto == null) {
366 if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
369 if (mMediaSet instanceof MtpDevice) {
375 //////////////////////////////////////////////////////////////////////////
376 // Action Bar show/hide management
377 //////////////////////////////////////////////////////////////////////////
379 private void showBars() {
380 if (mShowBars) return;
382 mOrientationManager.unlockOrientation();
384 mActivity.getGLRoot().setLightsOutMode(false);
385 refreshHidingMessage();
388 private void hideBars() {
389 if (!mShowBars) return;
392 mActivity.getGLRoot().setLightsOutMode(true);
393 mHandler.removeMessages(MSG_HIDE_BARS);
396 private void refreshHidingMessage() {
397 mHandler.removeMessages(MSG_HIDE_BARS);
398 if (!mIsMenuVisible) {
399 mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
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;
412 private void wantBars() {
413 if (canShowBars()) showBars();
416 private void toggleBars() {
420 if (canShowBars()) showBars();
424 private void updateBars() {
425 if (!canShowBars()) {
431 public void onOrientationCompensationChanged() {
432 mActivity.getGLRoot().requestLayoutContentPane();
436 protected void onBackPressed() {
439 } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) {
440 // We are leaving this page. Set the result now.
442 super.onBackPressed();
446 private void onUpPressed() {
447 if (mActivity.getStateManager().getStateCount() > 1) {
448 super.onBackPressed();
452 if (mOriginalSetPathString == null) return;
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);
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);
472 private void setResult() {
473 Intent result = null;
474 if (!mPhotoView.getFilmMode()) {
475 result = new Intent();
476 result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex);
478 setStateResult(Activity.RESULT_OK, result);
481 //////////////////////////////////////////////////////////////////////////
482 // AppBridge.Server interface
483 //////////////////////////////////////////////////////////////////////////
486 public void setCameraRelativeFrame(Rect frame) {
487 mPhotoView.setCameraRelativeFrame(frame);
491 public boolean switchWithCaptureAnimation(int offset) {
492 return mPhotoView.switchWithCaptureAnimation(offset);
496 public void setSwipingEnabled(boolean enabled) {
497 mPhotoView.setSwipingEnabled(enabled);
501 public void notifyScreenNailChanged() {
502 mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail());
503 mScreenNailSet.notifyChange();
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);
513 updateMenuOperations();
518 private MenuExecutor.ProgressListener mConfirmDialogListener =
519 new MenuExecutor.ProgressListener() {
521 public void onProgressUpdate(int index) {}
524 public void onProgressComplete(int result) {}
527 public void onConfirmDialogShown() {
528 mHandler.removeMessages(MSG_HIDE_BARS);
532 public void onConfirmDialogDismissed(boolean confirmed) {
533 refreshHidingMessage();
538 protected boolean onItemSelected(MenuItem item) {
539 refreshHidingMessage();
540 MediaItem current = mModel.getMediaItem(0);
542 if (current == null) {
543 // item is not ready, ignore
547 int currentIndex = mModel.getCurrentIndex();
548 Path path = current.getPath();
550 DataManager manager = mActivity.getDataManager();
551 int action = item.getItemId();
552 String confirmMsg = null;
554 case android.R.id.home: {
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);
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
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),
586 case R.id.action_details: {
590 showDetails(currentIndex);
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);
605 case R.id.action_import:
606 mSelectionManager.deSelectAll();
607 mSelectionManager.toggle(path);
608 mMenuExecutor.onMenuClicked(item, confirmMsg,
609 new ImportCompleteListener(mActivity));
616 private void hideDetails() {
617 mShowDetails = false;
618 mDetailsHelper.hide();
621 private void showDetails(int index) {
623 if (mDetailsHelper == null) {
624 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
625 mDetailsHelper.setCloseListener(new CloseListener() {
627 public void onClose() {
632 mDetailsHelper.reloadDetails(index);
633 mDetailsHelper.show();
636 ////////////////////////////////////////////////////////////////////////////
637 // Callbacks from PhotoView
638 ////////////////////////////////////////////////////////////////////////////
640 public void onSingleTapUp(int x, int y) {
641 if (mAppBridge != null) {
642 if (mAppBridge.onSingleTapUp(x, y)) return;
645 MediaItem item = mModel.getMediaItem(0);
646 if (item == null || item == mScreenNailItem) {
647 // item is not ready or it is camera preview, ignore
652 (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 0;
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);
664 playVideo((Activity) mActivity, item.getPlayUri(), item.getName());
671 public void lockOrientation() {
672 mHandler.sendEmptyMessage(MSG_LOCK_ORIENTATION);
676 public void unlockOrientation() {
677 mHandler.sendEmptyMessage(MSG_UNLOCK_ORIENTATION);
681 public void onActionBarAllowed(boolean allowed) {
682 mActionBarAllowed = allowed;
683 mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR);
687 public void onActionBarWanted() {
688 mHandler.sendEmptyMessage(MSG_WANT_BARS);
692 public void onFullScreenChanged(boolean full) {
693 Message m = mHandler.obtainMessage(
694 MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
698 public static void playVideo(Activity activity, Uri uri, String title) {
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();
711 private void setCurrentPhotoByIntent(Intent intent) {
712 if (intent == null) return;
713 Path path = mApplication.getDataManager()
714 .findPathByUri(intent.getData(), intent.getType());
716 mModel.setCurrentPhoto(path, mCurrentIndex);
721 protected void onStateResult(int requestCode, int resultCode, Intent data) {
722 mHasActivityResult = true;
723 switch (requestCode) {
725 setCurrentPhotoByIntent(data);
728 if (resultCode == Activity.RESULT_OK) {
729 setCurrentPhotoByIntent(data);
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();
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);
746 mModel.setCurrentPhoto(Path.fromString(path), index);
752 private class PreparePhotoFallback implements OnGLIdleListener {
753 private PhotoFallbackEffect mPhotoFallback = new PhotoFallbackEffect();
754 private boolean mResultReady = false;
756 public synchronized PhotoFallbackEffect get() {
757 while (!mResultReady) {
758 Utils.waitWithoutInterrupt(this);
760 return mPhotoFallback;
764 public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
765 mPhotoFallback = mPhotoView.buildFallbackEffect(mRootPane, canvas);
766 synchronized (this) {
774 private void preparePhotoFallbackView() {
775 GLRoot root = mActivity.getGLRoot();
776 PreparePhotoFallback task = new PreparePhotoFallback();
777 root.unlockRenderThread();
778 PhotoFallbackEffect anim;
780 root.addOnGLIdleListener(task);
783 root.lockRenderThread();
785 mActivity.getTransitionStore().put(
786 AlbumPage.KEY_RESUME_ANIMATION, anim);
790 public void onPause() {
794 mActivity.getGLRoot().unfreeze();
795 mHandler.removeMessages(MSG_UNFREEZE_GLROOT);
796 if (isFinishing()) preparePhotoFallbackView();
798 DetailsHelper.pause();
801 mHandler.removeMessages(MSG_HIDE_BARS);
802 mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
804 mMenuExecutor.pause();
808 public void onCurrentImageUpdated() {
809 mActivity.getGLRoot().unfreeze();
813 protected void onResume() {
815 mActivity.getGLRoot().freeze();
817 setContentPane(mRootPane);
821 if (mMenuVisibilityListener == null) {
822 mMenuVisibilityListener = new MyMenuVisibilityListener();
824 mActionBar.setDisplayOptions(mSetPathString != null, true);
825 mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
827 if (mAppBridge != null && !mHasActivityResult) {
828 mPhotoView.resetToFirstPicture();
830 mHasActivityResult = false;
831 mHandler.sendEmptyMessageDelayed(MSG_UNFREEZE_GLROOT, UNFREEZE_GLROOT_TIMEOUT);
835 protected void onDestroy() {
836 if (mAppBridge != null) {
837 mAppBridge.setServer(null);
838 mScreenNailItem.setScreenNail(null);
839 mAppBridge.detachScreenNail();
841 mScreenNailSet = null;
842 mScreenNailItem = null;
844 mOrientationManager.removeListener(this);
845 mActivity.getGLRoot().setOrientationSource(null);
847 // Remove all pending messages.
848 mHandler.removeCallbacksAndMessages(null);
852 private class MyDetailsSource implements DetailsSource {
856 public MediaDetails getDetails() {
857 return mModel.getMediaItem(0).getDetails();
862 return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
866 public int findIndex(int indexHint) {
872 public int getIndex() {