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.annotation.TargetApi;
20 import android.app.ActionBar.OnMenuVisibilityListener;
21 import android.app.Activity;
22 import android.content.ActivityNotFoundException;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.graphics.Rect;
27 import android.net.Uri;
28 import android.nfc.NfcAdapter;
29 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
30 import android.nfc.NfcEvent;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.os.SystemClock;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.widget.RelativeLayout;
38 import android.widget.Toast;
40 import com.android.camera.CameraActivity;
41 import com.android.camera.ProxyLauncher;
42 import com.android.gallery3d.R;
43 import com.android.gallery3d.common.ApiHelper;
44 import com.android.gallery3d.data.ComboAlbum;
45 import com.android.gallery3d.data.DataManager;
46 import com.android.gallery3d.data.FilterDeleteSet;
47 import com.android.gallery3d.data.FilterSource;
48 import com.android.gallery3d.data.LocalImage;
49 import com.android.gallery3d.data.MediaDetails;
50 import com.android.gallery3d.data.MediaItem;
51 import com.android.gallery3d.data.MediaObject;
52 import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
53 import com.android.gallery3d.data.MediaSet;
54 import com.android.gallery3d.data.MtpSource;
55 import com.android.gallery3d.data.Path;
56 import com.android.gallery3d.data.SecureAlbum;
57 import com.android.gallery3d.data.SecureSource;
58 import com.android.gallery3d.data.SnailAlbum;
59 import com.android.gallery3d.data.SnailItem;
60 import com.android.gallery3d.data.SnailSource;
61 import com.android.gallery3d.filtershow.FilterShowActivity;
62 import com.android.gallery3d.picasasource.PicasaSource;
63 import com.android.gallery3d.ui.DetailsHelper;
64 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
65 import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
66 import com.android.gallery3d.ui.GLView;
67 import com.android.gallery3d.ui.ImportCompleteListener;
68 import com.android.gallery3d.ui.MenuExecutor;
69 import com.android.gallery3d.ui.PhotoView;
70 import com.android.gallery3d.ui.SelectionManager;
71 import com.android.gallery3d.ui.SynchronizedHandler;
72 import com.android.gallery3d.util.GalleryUtils;
74 public abstract class PhotoPage extends ActivityState implements
75 PhotoView.Listener, AppBridge.Server,
76 PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener {
77 private static final String TAG = "PhotoPage";
79 private static final int MSG_HIDE_BARS = 1;
80 private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
81 private static final int MSG_UPDATE_ACTION_BAR = 5;
82 private static final int MSG_UNFREEZE_GLROOT = 6;
83 private static final int MSG_WANT_BARS = 7;
84 private static final int MSG_REFRESH_BOTTOM_CONTROLS = 8;
85 private static final int MSG_ON_CAMERA_CENTER = 9;
86 private static final int MSG_ON_PICTURE_CENTER = 10;
87 private static final int MSG_REFRESH_IMAGE = 11;
88 private static final int MSG_UPDATE_PHOTO_UI = 12;
89 private static final int MSG_UPDATE_PROGRESS = 13;
90 private static final int MSG_UPDATE_DEFERRED = 14;
91 private static final int MSG_UPDATE_SHARE_URI = 15;
92 private static final int MSG_UPDATE_PANORAMA_UI = 16;
94 private static final int HIDE_BARS_TIMEOUT = 3500;
95 private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
97 private static final int REQUEST_SLIDESHOW = 1;
98 private static final int REQUEST_CROP = 2;
99 private static final int REQUEST_CROP_PICASA = 3;
100 private static final int REQUEST_EDIT = 4;
101 private static final int REQUEST_PLAY_VIDEO = 5;
102 private static final int REQUEST_TRIM = 6;
104 public static final String KEY_MEDIA_SET_PATH = "media-set-path";
105 public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
106 public static final String KEY_INDEX_HINT = "index-hint";
107 public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
108 public static final String KEY_APP_BRIDGE = "app-bridge";
109 public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up";
110 public static final String KEY_START_IN_FILMSTRIP = "start-in-filmstrip";
111 public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
112 public static final String KEY_SHOW_WHEN_LOCKED = "show_when_locked";
113 public static final String KEY_IN_CAMERA_ROLL = "in_camera_roll";
115 public static final String KEY_ALBUMPAGE_TRANSITION = "albumpage-transition";
116 public static final int MSG_ALBUMPAGE_NONE = 0;
117 public static final int MSG_ALBUMPAGE_STARTED = 1;
118 public static final int MSG_ALBUMPAGE_RESUMED = 2;
119 public static final int MSG_ALBUMPAGE_PICKED = 4;
121 public static final String ACTION_NEXTGEN_EDIT = "action_nextgen_edit";
123 private GalleryApp mApplication;
124 private SelectionManager mSelectionManager;
126 private PhotoView mPhotoView;
127 private PhotoPage.Model mModel;
128 private DetailsHelper mDetailsHelper;
129 private boolean mShowDetails;
131 // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
132 // E.g., viewing a photo in gmail attachment
133 private FilterDeleteSet mMediaSet;
135 // The mediaset used by camera launched from secure lock screen.
136 private SecureAlbum mSecureAlbum;
138 private int mCurrentIndex = 0;
139 private Handler mHandler;
140 private boolean mShowBars = true;
141 private volatile boolean mActionBarAllowed = true;
142 private GalleryActionBar mActionBar;
143 private boolean mIsMenuVisible;
144 private boolean mHaveImageEditor;
145 private PhotoPageBottomControls mBottomControls;
146 private PhotoPageProgressBar mProgressBar;
147 private MediaItem mCurrentPhoto = null;
148 private MenuExecutor mMenuExecutor;
149 private boolean mIsActive;
150 private boolean mShowSpinner;
151 private String mSetPathString;
152 // This is the original mSetPathString before adding the camera preview item.
153 private String mOriginalSetPathString;
154 private AppBridge mAppBridge;
155 private SnailItem mScreenNailItem;
156 private SnailAlbum mScreenNailSet;
157 private OrientationManager mOrientationManager;
158 private boolean mTreatBackAsUp;
159 private boolean mStartInFilmstrip;
160 private boolean mHasCameraScreennailOrPlaceholder = false;
161 private boolean mRecenterCameraOnResume = true;
163 // These are only valid after the panorama callback
164 private boolean mIsPanorama;
165 private boolean mIsPanorama360;
167 private long mCameraSwitchCutoff = 0;
168 private boolean mSkipUpdateCurrentPhoto = false;
169 private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300;
171 private static final long DEFERRED_UPDATE_MS = 250;
172 private boolean mDeferredUpdateWaiting = false;
173 private long mDeferUpdateUntil = Long.MAX_VALUE;
175 // The item that is deleted (but it can still be undeleted before commiting)
176 private Path mDeletePath;
177 private boolean mDeleteIsFocus; // whether the deleted item was in focus
179 private Uri[] mNfcPushUris = new Uri[1];
181 private final MyMenuVisibilityListener mMenuVisibilityListener =
182 new MyMenuVisibilityListener();
183 private UpdateProgressListener mProgressListener;
185 private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() {
187 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
188 boolean isPanorama360) {
189 if (mediaObject == mCurrentPhoto) {
190 mHandler.obtainMessage(MSG_UPDATE_PANORAMA_UI, isPanorama360 ? 1 : 0, 0,
191 mediaObject).sendToTarget();
196 private final PanoramaSupportCallback mRefreshBottomControlsCallback = new PanoramaSupportCallback() {
198 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
199 boolean isPanorama360) {
200 if (mediaObject == mCurrentPhoto) {
201 mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, isPanorama ? 1 : 0, isPanorama360 ? 1 : 0,
202 mediaObject).sendToTarget();
207 private final PanoramaSupportCallback mUpdateShareURICallback = new PanoramaSupportCallback() {
209 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
210 boolean isPanorama360) {
211 if (mediaObject == mCurrentPhoto) {
212 mHandler.obtainMessage(MSG_UPDATE_SHARE_URI, isPanorama360 ? 1 : 0, 0, mediaObject)
218 public static interface Model extends PhotoView.Model {
219 public void resume();
221 public boolean isEmpty();
222 public void setCurrentPhoto(Path path, int indexHint);
225 private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
227 public void onMenuVisibilityChanged(boolean isVisible) {
228 mIsMenuVisible = isVisible;
229 refreshHidingMessage();
233 private class UpdateProgressListener implements StitchingChangeListener {
236 public void onStitchingResult(Uri uri) {
237 sendUpdate(uri, MSG_REFRESH_IMAGE);
241 public void onStitchingQueued(Uri uri) {
242 sendUpdate(uri, MSG_UPDATE_PROGRESS);
246 public void onStitchingProgress(Uri uri, final int progress) {
247 sendUpdate(uri, MSG_UPDATE_PROGRESS);
250 private void sendUpdate(Uri uri, int message) {
251 MediaObject currentPhoto = mCurrentPhoto;
252 boolean isCurrentPhoto = currentPhoto instanceof LocalImage
253 && currentPhoto.getContentUri().equals(uri);
254 if (isCurrentPhoto) {
255 mHandler.sendEmptyMessage(message);
261 protected int getBackgroundColorId() {
262 return R.color.photo_background;
265 private final GLView mRootPane = new GLView() {
267 protected void onLayout(
268 boolean changed, int left, int top, int right, int bottom) {
269 mPhotoView.layout(0, 0, right - left, bottom - top);
271 mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom);
277 public void onCreate(Bundle data, Bundle restoreState) {
278 super.onCreate(data, restoreState);
279 mActionBar = mActivity.getGalleryActionBar();
280 mSelectionManager = new SelectionManager(mActivity, false);
281 mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager);
283 mPhotoView = new PhotoView(mActivity);
284 mPhotoView.setListener(this);
285 mRootPane.addComponent(mPhotoView);
286 mApplication = (GalleryApp) ((Activity) mActivity).getApplication();
287 mOrientationManager = mActivity.getOrientationManager();
288 mActivity.getGLRoot().setOrientationSource(mOrientationManager);
290 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
292 public void handleMessage(Message message) {
293 switch (message.what) {
294 case MSG_HIDE_BARS: {
298 case MSG_REFRESH_BOTTOM_CONTROLS: {
299 if (mCurrentPhoto == message.obj && mBottomControls != null) {
300 mIsPanorama = message.arg1 == 1;
301 mIsPanorama360 = message.arg2 == 1;
302 mBottomControls.refresh();
306 case MSG_ON_FULL_SCREEN_CHANGED: {
307 if (mAppBridge != null) {
308 mAppBridge.onFullScreenChanged(message.arg1 == 1);
312 case MSG_UPDATE_ACTION_BAR: {
316 case MSG_WANT_BARS: {
320 case MSG_UNFREEZE_GLROOT: {
321 mActivity.getGLRoot().unfreeze();
324 case MSG_UPDATE_DEFERRED: {
325 long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis();
326 if (nextUpdate <= 0) {
327 mDeferredUpdateWaiting = false;
328 updateUIForCurrentPhoto();
330 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate);
334 case MSG_ON_CAMERA_CENTER: {
335 mSkipUpdateCurrentPhoto = false;
336 boolean stayedOnCamera = false;
337 if (!mPhotoView.getFilmMode()) {
338 stayedOnCamera = true;
339 } else if (SystemClock.uptimeMillis() < mCameraSwitchCutoff &&
340 mMediaSet.getMediaItemCount() > 1) {
341 mPhotoView.switchToImage(1);
343 if (mAppBridge != null) mPhotoView.setFilmMode(false);
344 stayedOnCamera = true;
347 if (stayedOnCamera) {
348 if (mAppBridge == null && mMediaSet.getTotalMediaItemCount() > 1) {
350 /* We got here by swiping from photo 1 to the
351 placeholder, so make it be the thing that
352 is in focus when the user presses back from
354 mPhotoView.switchToImage(1);
357 updateCurrentPhoto(mModel.getMediaItem(0));
362 case MSG_ON_PICTURE_CENTER: {
363 if (!mPhotoView.getFilmMode() && mCurrentPhoto != null
364 && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) {
365 mPhotoView.setFilmMode(true);
369 case MSG_REFRESH_IMAGE: {
370 final MediaItem photo = mCurrentPhoto;
371 mCurrentPhoto = null;
372 updateCurrentPhoto(photo);
375 case MSG_UPDATE_PHOTO_UI: {
376 updateUIForCurrentPhoto();
379 case MSG_UPDATE_PROGRESS: {
383 case MSG_UPDATE_SHARE_URI: {
384 if (mCurrentPhoto == message.obj) {
385 boolean isPanorama360 = message.arg1 != 0;
386 Uri contentUri = mCurrentPhoto.getContentUri();
387 Intent panoramaIntent = null;
389 panoramaIntent = createSharePanoramaIntent(contentUri);
391 Intent shareIntent = createShareIntent(mCurrentPhoto);
393 mActionBar.setShareIntents(panoramaIntent, shareIntent);
394 setNfcBeamPushUri(contentUri);
398 case MSG_UPDATE_PANORAMA_UI: {
399 if (mCurrentPhoto == message.obj) {
400 boolean isPanorama360 = message.arg1 != 0;
401 updatePanoramaUI(isPanorama360);
405 default: throw new AssertionError(message.what);
410 mSetPathString = data.getString(KEY_MEDIA_SET_PATH);
411 mOriginalSetPathString = mSetPathString;
413 String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH);
414 Path itemPath = itemPathString != null ?
415 Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)) :
417 mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false);
418 mStartInFilmstrip = data.getBoolean(KEY_START_IN_FILMSTRIP, false);
419 boolean inCameraRoll = data.getBoolean(KEY_IN_CAMERA_ROLL, false);
420 mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
421 if (mSetPathString != null) {
423 mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
424 if (mAppBridge != null) {
426 mHasCameraScreennailOrPlaceholder = true;
427 mAppBridge.setServer(this);
429 // Get the ScreenNail from AppBridge and register it.
430 int id = SnailSource.newId();
431 Path screenNailSetPath = SnailSource.getSetPath(id);
432 Path screenNailItemPath = SnailSource.getItemPath(id);
433 mScreenNailSet = (SnailAlbum) mActivity.getDataManager()
434 .getMediaObject(screenNailSetPath);
435 mScreenNailItem = (SnailItem) mActivity.getDataManager()
436 .getMediaObject(screenNailItemPath);
437 mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail());
439 if (data.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) {
440 // Set the flag to be on top of the lock screen.
441 mFlags |= FLAG_SHOW_WHEN_LOCKED;
444 // Don't display "empty album" action item for capture intents.
445 if (!mSetPathString.equals("/local/all/0")) {
446 // Check if the path is a secure album.
447 if (SecureSource.isSecurePath(mSetPathString)) {
448 mSecureAlbum = (SecureAlbum) mActivity.getDataManager()
449 .getMediaSet(mSetPathString);
450 mShowSpinner = false;
452 mSetPathString = "/filter/empty/{"+mSetPathString+"}";
455 // Combine the original MediaSet with the one for ScreenNail
457 mSetPathString = "/combo/item/{" + screenNailSetPath +
458 "," + mSetPathString + "}";
460 // Start from the screen nail.
461 itemPath = screenNailItemPath;
462 } else if (inCameraRoll && GalleryUtils.isCameraAvailable(mActivity)) {
463 mSetPathString = "/combo/item/{" + FilterSource.FILTER_CAMERA_SHORTCUT +
464 "," + mSetPathString + "}";
466 mHasCameraScreennailOrPlaceholder = true;
469 MediaSet originalSet = mActivity.getDataManager()
470 .getMediaSet(mSetPathString);
471 if (mHasCameraScreennailOrPlaceholder && originalSet instanceof ComboAlbum) {
472 // Use the name of the camera album rather than the default
473 // ComboAlbum behavior
474 ((ComboAlbum) originalSet).useNameOfChild(1);
476 mSelectionManager.setSourceMediaSet(originalSet);
477 mSetPathString = "/filter/delete/{" + mSetPathString + "}";
478 mMediaSet = (FilterDeleteSet) mActivity.getDataManager()
479 .getMediaSet(mSetPathString);
480 if (mMediaSet == null) {
481 Log.w(TAG, "failed to restore " + mSetPathString);
483 if (itemPath == null) {
484 int mediaItemCount = mMediaSet.getMediaItemCount();
485 if (mediaItemCount > 0) {
486 if (mCurrentIndex >= mediaItemCount) mCurrentIndex = 0;
487 itemPath = mMediaSet.getMediaItem(mCurrentIndex, 1)
490 // Bail out, PhotoPage can't load on an empty album
494 PhotoDataAdapter pda = new PhotoDataAdapter(
495 mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex,
496 mAppBridge == null ? -1 : 0,
497 mAppBridge == null ? false : mAppBridge.isPanorama(),
498 mAppBridge == null ? false : mAppBridge.isStaticCamera());
500 mPhotoView.setModel(mModel);
502 pda.setDataListener(new PhotoDataAdapter.DataListener() {
505 public void onPhotoChanged(int index, Path item) {
506 int oldIndex = mCurrentIndex;
507 mCurrentIndex = index;
509 if (mHasCameraScreennailOrPlaceholder) {
510 if (mCurrentIndex > 0) {
511 mSkipUpdateCurrentPhoto = false;
514 if (oldIndex == 0 && mCurrentIndex > 0
515 && !mPhotoView.getFilmMode()) {
516 mPhotoView.setFilmMode(true);
517 } else if (oldIndex == 2 && mCurrentIndex == 1) {
518 mCameraSwitchCutoff = SystemClock.uptimeMillis() +
519 CAMERA_SWITCH_CUTOFF_THRESHOLD_MS;
520 mPhotoView.stopScrolling();
521 } else if (oldIndex >= 1 && mCurrentIndex == 0) {
522 mPhotoView.setWantPictureCenterCallbacks(true);
523 mSkipUpdateCurrentPhoto = true;
526 if (!mSkipUpdateCurrentPhoto) {
528 MediaItem photo = mModel.getMediaItem(0);
529 if (photo != null) updateCurrentPhoto(photo);
533 // Reset the timeout for the bars after a swipe
534 refreshHidingMessage();
538 public void onLoadingFinished(boolean loadingFailed) {
539 if (!mModel.isEmpty()) {
540 MediaItem photo = mModel.getMediaItem(0);
541 if (photo != null) updateCurrentPhoto(photo);
542 } else if (mIsActive) {
543 // We only want to finish the PhotoPage if there is no
544 // deletion that the user can undo.
545 if (mMediaSet.getNumberOfDeletions() == 0) {
546 mActivity.getStateManager().finishState(
553 public void onLoadingStarted() {
557 // Get default media set by the URI
558 MediaItem mediaItem = (MediaItem)
559 mActivity.getDataManager().getMediaObject(itemPath);
560 mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem);
561 mPhotoView.setModel(mModel);
562 updateCurrentPhoto(mediaItem);
563 mShowSpinner = false;
566 mPhotoView.setFilmMode(mStartInFilmstrip && mMediaSet.getMediaItemCount() > 1);
567 RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity)
568 .findViewById(mAppBridge != null ? R.id.content : R.id.gallery_root);
569 if (galleryRoot != null) {
570 if (mSecureAlbum == null) {
571 mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot);
573 StitchingProgressManager progressManager = mApplication.getStitchingProgressManager();
574 if (progressManager != null) {
575 mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot);
576 mProgressListener = new UpdateProgressListener();
577 progressManager.addChangeListener(mProgressListener);
578 if (mSecureAlbum != null) {
579 progressManager.addChangeListener(mSecureAlbum);
586 public void onPictureCenter(boolean isCamera) {
587 isCamera = isCamera || (mHasCameraScreennailOrPlaceholder && mAppBridge == null);
588 mPhotoView.setWantPictureCenterCallbacks(false);
589 mHandler.removeMessages(MSG_ON_CAMERA_CENTER);
590 mHandler.removeMessages(MSG_ON_PICTURE_CENTER);
591 mHandler.sendEmptyMessage(isCamera ? MSG_ON_CAMERA_CENTER : MSG_ON_PICTURE_CENTER);
595 public boolean canDisplayBottomControls() {
596 return mIsActive && !mPhotoView.canUndo();
600 public boolean canDisplayBottomControl(int control) {
601 if (mCurrentPhoto == null) {
605 case R.id.photopage_bottom_control_edit:
606 return mHaveImageEditor && mShowBars
607 && !mPhotoView.getFilmMode()
608 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0
609 && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE;
610 case R.id.photopage_bottom_control_panorama:
612 case R.id.photopage_bottom_control_tiny_planet:
613 return mHaveImageEditor && mShowBars
614 && mIsPanorama360 && !mPhotoView.getFilmMode();
621 public void onBottomControlClicked(int control) {
623 case R.id.photopage_bottom_control_edit:
626 case R.id.photopage_bottom_control_panorama:
627 mActivity.getPanoramaViewHelper()
628 .showPanorama(mCurrentPhoto.getContentUri());
630 case R.id.photopage_bottom_control_tiny_planet:
638 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
639 private void setupNfcBeamPush() {
640 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return;
642 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mActivity);
643 if (adapter != null) {
644 adapter.setBeamPushUris(null, mActivity);
645 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
647 public Uri[] createBeamUris(NfcEvent event) {
654 private void setNfcBeamPushUri(Uri uri) {
655 mNfcPushUris[0] = uri;
658 private static Intent createShareIntent(MediaObject mediaObject) {
659 int type = mediaObject.getMediaType();
660 return new Intent(Intent.ACTION_SEND)
661 .setType(MenuExecutor.getMimeType(type))
662 .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri())
663 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
666 private static Intent createSharePanoramaIntent(Uri contentUri) {
667 return new Intent(Intent.ACTION_SEND)
668 .setType(GalleryUtils.MIME_TYPE_PANORAMA360)
669 .putExtra(Intent.EXTRA_STREAM, contentUri)
670 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
673 private void overrideTransitionToEditor() {
674 ((Activity) mActivity).overridePendingTransition(android.R.anim.slide_in_left,
675 android.R.anim.fade_out);
678 private void launchTinyPlanet() {
679 // Deep link into tiny planet
680 MediaItem current = mModel.getMediaItem(0);
681 Intent intent = new Intent(FilterShowActivity.TINY_PLANET_ACTION);
682 intent.setClass(mActivity, FilterShowActivity.class);
683 intent.setDataAndType(current.getContentUri(), current.getMimeType())
684 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
685 intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN,
686 mActivity.isFullscreen());
687 mActivity.startActivityForResult(intent, REQUEST_EDIT);
688 overrideTransitionToEditor();
691 private void launchCamera() {
692 Intent intent = new Intent(mActivity, CameraActivity.class)
693 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
694 mRecenterCameraOnResume = false;
695 mActivity.startActivity(intent);
698 private void launchPhotoEditor() {
699 MediaItem current = mModel.getMediaItem(0);
700 if (current == null || (current.getSupportedOperations()
701 & MediaObject.SUPPORT_EDIT) == 0) {
705 Intent intent = new Intent(ACTION_NEXTGEN_EDIT);
707 intent.setDataAndType(current.getContentUri(), current.getMimeType())
708 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
709 if (mActivity.getPackageManager()
710 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) {
711 intent.setAction(Intent.ACTION_EDIT);
713 intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN,
714 mActivity.isFullscreen());
715 ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null),
717 overrideTransitionToEditor();
720 private void requestDeferredUpdate() {
721 mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS;
722 if (!mDeferredUpdateWaiting) {
723 mDeferredUpdateWaiting = true;
724 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS);
728 private void updateUIForCurrentPhoto() {
729 if (mCurrentPhoto == null) return;
731 // If by swiping or deletion the user ends up on an action item
732 // and zoomed in, zoom out so that the context of the action is
734 if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0
735 && !mPhotoView.getFilmMode()) {
736 mPhotoView.setWantPictureCenterCallbacks(true);
739 updateMenuOperations();
740 refreshBottomControlsWhenReady();
742 mDetailsHelper.reloadDetails();
744 if ((mSecureAlbum == null)
745 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
746 mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback);
751 private void updateCurrentPhoto(MediaItem photo) {
752 if (mCurrentPhoto == photo) return;
753 mCurrentPhoto = photo;
754 if (mPhotoView.getFilmMode()) {
755 requestDeferredUpdate();
757 updateUIForCurrentPhoto();
761 private void updateProgressBar() {
762 if (mProgressBar != null) {
763 mProgressBar.hideProgress();
764 StitchingProgressManager progressManager = mApplication.getStitchingProgressManager();
765 if (progressManager != null && mCurrentPhoto instanceof LocalImage) {
766 Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri());
767 if (progress != null) {
768 mProgressBar.setProgress(progress);
774 private void updateMenuOperations() {
775 Menu menu = mActionBar.getMenu();
777 // it could be null if onCreateActionBar has not been called yet
778 if (menu == null) return;
780 MenuItem item = menu.findItem(R.id.action_slideshow);
782 item.setVisible((mSecureAlbum == null) && canDoSlideShow());
784 if (mCurrentPhoto == null) return;
786 int supportedOperations = mCurrentPhoto.getSupportedOperations();
787 if (mSecureAlbum != null) {
788 supportedOperations &= MediaObject.SUPPORT_DELETE;
789 } else if (!mHaveImageEditor) {
790 supportedOperations &= ~MediaObject.SUPPORT_EDIT;
792 MenuExecutor.updateMenuOperation(menu, supportedOperations);
793 mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback);
796 private boolean canDoSlideShow() {
797 if (mMediaSet == null || mCurrentPhoto == null) {
800 if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
803 if (MtpSource.isMtpPath(mOriginalSetPathString)) {
809 //////////////////////////////////////////////////////////////////////////
810 // Action Bar show/hide management
811 //////////////////////////////////////////////////////////////////////////
813 private void showBars() {
814 if (mShowBars) return;
816 mOrientationManager.unlockOrientation();
818 mActivity.getGLRoot().setLightsOutMode(false);
819 refreshHidingMessage();
820 refreshBottomControlsWhenReady();
823 private void hideBars() {
824 if (!mShowBars) return;
827 mActivity.getGLRoot().setLightsOutMode(true);
828 mHandler.removeMessages(MSG_HIDE_BARS);
829 refreshBottomControlsWhenReady();
832 private void refreshHidingMessage() {
833 mHandler.removeMessages(MSG_HIDE_BARS);
834 if (!mIsMenuVisible && !mPhotoView.getFilmMode()) {
835 mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
839 private boolean canShowBars() {
840 // No bars if we are showing camera preview.
841 if (mAppBridge != null && mCurrentIndex == 0
842 && !mPhotoView.getFilmMode()) return false;
844 // No bars if it's not allowed.
845 if (!mActionBarAllowed) return false;
850 private void wantBars() {
851 if (canShowBars()) showBars();
854 private void toggleBars() {
858 if (canShowBars()) showBars();
862 private void updateBars() {
863 if (!canShowBars()) {
869 protected void onBackPressed() {
872 } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) {
873 // We are leaving this page. Set the result now.
875 if (mStartInFilmstrip && !mPhotoView.getFilmMode()) {
876 mPhotoView.setFilmMode(true);
877 } else if (mTreatBackAsUp) {
880 super.onBackPressed();
885 private void onUpPressed() {
886 if ((mStartInFilmstrip || mAppBridge != null)
887 && !mPhotoView.getFilmMode()) {
888 mPhotoView.setFilmMode(true);
892 if (mActivity.getStateManager().getStateCount() > 1) {
894 super.onBackPressed();
898 if (mOriginalSetPathString == null) return;
900 if (mAppBridge == null) {
901 // We're in view mode so set up the stacks on our own.
902 Bundle data = new Bundle(getData());
903 data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
904 data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
905 mActivity.getDataManager().getTopSetPath(
906 DataManager.INCLUDE_ALL));
907 mActivity.getStateManager().switchState(this, AlbumPage.class, data);
909 GalleryUtils.startGalleryActivity(mActivity);
913 private void setResult() {
914 Intent result = null;
915 result = new Intent();
916 result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex);
917 setStateResult(Activity.RESULT_OK, result);
920 //////////////////////////////////////////////////////////////////////////
921 // AppBridge.Server interface
922 //////////////////////////////////////////////////////////////////////////
925 public void setCameraRelativeFrame(Rect frame) {
926 mPhotoView.setCameraRelativeFrame(frame);
930 public boolean switchWithCaptureAnimation(int offset) {
931 return mPhotoView.switchWithCaptureAnimation(offset);
935 public void setSwipingEnabled(boolean enabled) {
936 mPhotoView.setSwipingEnabled(enabled);
940 public void notifyScreenNailChanged() {
941 mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail());
942 mScreenNailSet.notifyChange();
946 public void addSecureAlbumItem(boolean isVideo, int id) {
947 mSecureAlbum.addMediaItem(isVideo, id);
951 protected boolean onCreateActionBar(Menu menu) {
952 mActionBar.createActionBarMenu(R.menu.photo, menu);
953 mHaveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*");
954 updateMenuOperations();
955 mActionBar.setTitle(mMediaSet != null ? mMediaSet.getName() : "");
959 private MenuExecutor.ProgressListener mConfirmDialogListener =
960 new MenuExecutor.ProgressListener() {
962 public void onProgressUpdate(int index) {}
965 public void onProgressComplete(int result) {}
968 public void onConfirmDialogShown() {
969 mHandler.removeMessages(MSG_HIDE_BARS);
973 public void onConfirmDialogDismissed(boolean confirmed) {
974 refreshHidingMessage();
978 public void onProgressStart() {}
981 private void switchToGrid() {
982 if (mActivity.getStateManager().hasStateClass(AlbumPage.class)) {
985 if (mOriginalSetPathString == null) return;
986 if (mProgressBar != null) {
987 updateCurrentPhoto(null);
988 mProgressBar.hideProgress();
990 Bundle data = new Bundle(getData());
991 data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
992 data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
993 mActivity.getDataManager().getTopSetPath(
994 DataManager.INCLUDE_ALL));
996 // We only show cluster menu in the first AlbumPage in stack
997 // TODO: Enable this when running from the camera app
998 boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
999 data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum
1000 && mAppBridge == null);
1002 data.putBoolean(PhotoPage.KEY_APP_BRIDGE, mAppBridge != null);
1004 // Account for live preview being first item
1005 mActivity.getTransitionStore().put(KEY_RETURN_INDEX_HINT,
1006 mAppBridge != null ? mCurrentIndex - 1 : mCurrentIndex);
1008 if (mHasCameraScreennailOrPlaceholder && mAppBridge != null) {
1009 mActivity.getStateManager().startState(AlbumPage.class, data);
1011 mActivity.getStateManager().switchState(this, AlbumPage.class, data);
1017 protected boolean onItemSelected(MenuItem item) {
1018 if (mModel == null) return true;
1019 refreshHidingMessage();
1020 MediaItem current = mModel.getMediaItem(0);
1022 // This is a shield for monkey when it clicks the action bar
1023 // menu when transitioning from filmstrip to camera
1024 if (current instanceof SnailItem) return true;
1025 // TODO: We should check the current photo against the MediaItem
1026 // that the menu was initially created for. We need to fix this
1027 // after PhotoPage being refactored.
1028 if (current == null) {
1029 // item is not ready, ignore
1032 int currentIndex = mModel.getCurrentIndex();
1033 Path path = current.getPath();
1035 DataManager manager = mActivity.getDataManager();
1036 int action = item.getItemId();
1037 String confirmMsg = null;
1039 case android.R.id.home: {
1043 case R.id.action_slideshow: {
1044 Bundle data = new Bundle();
1045 data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString());
1046 data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString());
1047 data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex);
1048 data.putBoolean(SlideshowPage.KEY_REPEAT, true);
1049 mActivity.getStateManager().startStateForResult(
1050 SlideshowPage.class, REQUEST_SLIDESHOW, data);
1053 case R.id.action_crop: {
1054 Activity activity = mActivity;
1055 Intent intent = new Intent(FilterShowActivity.CROP_ACTION);
1056 intent.setClass(activity, FilterShowActivity.class);
1057 intent.setDataAndType(manager.getContentUri(path), current.getMimeType())
1058 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1059 activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
1060 ? REQUEST_CROP_PICASA
1064 case R.id.action_trim: {
1065 Intent intent = new Intent(mActivity, TrimVideo.class);
1066 intent.setData(manager.getContentUri(path));
1067 // We need the file path to wrap this into a RandomAccessFile.
1068 intent.putExtra(KEY_MEDIA_ITEM_PATH, current.getFilePath());
1069 mActivity.startActivityForResult(intent, REQUEST_TRIM);
1072 case R.id.action_mute: {
1073 MuteVideo muteVideo = new MuteVideo(current,
1074 manager.getContentUri(path), mActivity);
1075 muteVideo.muteInBackground();
1078 case R.id.action_edit: {
1079 launchPhotoEditor();
1082 case R.id.action_details: {
1090 case R.id.action_delete:
1091 confirmMsg = mActivity.getResources().getQuantityString(
1092 R.plurals.delete_selection, 1);
1093 case R.id.action_setas:
1094 case R.id.action_rotate_ccw:
1095 case R.id.action_rotate_cw:
1096 case R.id.action_show_on_map:
1097 mSelectionManager.deSelectAll();
1098 mSelectionManager.toggle(path);
1099 mMenuExecutor.onMenuClicked(item, confirmMsg, mConfirmDialogListener);
1101 case R.id.action_import:
1102 mSelectionManager.deSelectAll();
1103 mSelectionManager.toggle(path);
1104 mMenuExecutor.onMenuClicked(item, confirmMsg,
1105 new ImportCompleteListener(mActivity));
1112 private void hideDetails() {
1113 mShowDetails = false;
1114 mDetailsHelper.hide();
1117 private void showDetails() {
1118 mShowDetails = true;
1119 if (mDetailsHelper == null) {
1120 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
1121 mDetailsHelper.setCloseListener(new CloseListener() {
1123 public void onClose() {
1128 mDetailsHelper.show();
1131 ////////////////////////////////////////////////////////////////////////////
1132 // Callbacks from PhotoView
1133 ////////////////////////////////////////////////////////////////////////////
1135 public void onSingleTapUp(int x, int y) {
1136 if (mAppBridge != null) {
1137 if (mAppBridge.onSingleTapUp(x, y)) return;
1140 MediaItem item = mModel.getMediaItem(0);
1141 if (item == null || item == mScreenNailItem) {
1142 // item is not ready or it is camera preview, ignore
1146 int supported = item.getSupportedOperations();
1147 boolean playVideo = ((supported & MediaItem.SUPPORT_PLAY) != 0);
1148 boolean unlock = ((supported & MediaItem.SUPPORT_UNLOCK) != 0);
1149 boolean goBack = ((supported & MediaItem.SUPPORT_BACK) != 0);
1150 boolean launchCamera = ((supported & MediaItem.SUPPORT_CAMERA_SHORTCUT) != 0);
1153 // determine if the point is at center (1/6) of the photo view.
1154 // (The position of the "play" icon is at center (1/6) of the photo)
1155 int w = mPhotoView.getWidth();
1156 int h = mPhotoView.getHeight();
1157 playVideo = (Math.abs(x - w / 2) * 12 <= w)
1158 && (Math.abs(y - h / 2) * 12 <= h);
1162 if (mSecureAlbum == null) {
1163 playVideo(mActivity, item.getPlayUri(), item.getName());
1165 mActivity.getStateManager().finishState(this);
1167 } else if (goBack) {
1169 } else if (unlock) {
1170 Intent intent = new Intent(mActivity, Gallery.class);
1171 intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true);
1172 mActivity.startActivity(intent);
1173 } else if (launchCamera) {
1181 public void onActionBarAllowed(boolean allowed) {
1182 mActionBarAllowed = allowed;
1183 mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR);
1187 public void onActionBarWanted() {
1188 mHandler.sendEmptyMessage(MSG_WANT_BARS);
1192 public void onFullScreenChanged(boolean full) {
1193 Message m = mHandler.obtainMessage(
1194 MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
1198 // How we do delete/undo:
1200 // When the user choose to delete a media item, we just tell the
1201 // FilterDeleteSet to hide that item. If the user choose to undo it, we
1202 // again tell FilterDeleteSet not to hide it. If the user choose to commit
1203 // the deletion, we then actually delete the media item.
1205 public void onDeleteImage(Path path, int offset) {
1206 onCommitDeleteImage(); // commit the previous deletion
1208 mDeleteIsFocus = (offset == 0);
1209 mMediaSet.addDeletion(path, mCurrentIndex + offset);
1213 public void onUndoDeleteImage() {
1214 if (mDeletePath == null) return;
1215 // If the deletion was done on the focused item, we want the model to
1216 // focus on it when it is undeleted.
1217 if (mDeleteIsFocus) mModel.setFocusHintPath(mDeletePath);
1218 mMediaSet.removeDeletion(mDeletePath);
1223 public void onCommitDeleteImage() {
1224 if (mDeletePath == null) return;
1225 mSelectionManager.deSelectAll();
1226 mSelectionManager.toggle(mDeletePath);
1227 mMenuExecutor.onMenuClicked(R.id.action_delete, null, true, false);
1231 public void playVideo(Activity activity, Uri uri, String title) {
1233 Intent intent = new Intent(Intent.ACTION_VIEW)
1234 .setDataAndType(uri, "video/*")
1235 .putExtra(Intent.EXTRA_TITLE, title)
1236 .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true);
1237 activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO);
1238 } catch (ActivityNotFoundException e) {
1239 Toast.makeText(activity, activity.getString(R.string.video_err),
1240 Toast.LENGTH_SHORT).show();
1244 private void setCurrentPhotoByIntent(Intent intent) {
1245 if (intent == null) return;
1246 Path path = mApplication.getDataManager()
1247 .findPathByUri(intent.getData(), intent.getType());
1249 Path albumPath = mApplication.getDataManager().getDefaultSetOf(path);
1250 if (!albumPath.equalsIgnoreCase(mOriginalSetPathString)) {
1251 // If the edited image is stored in a different album, we need
1252 // to start a new activity state to show the new image
1253 Bundle data = new Bundle(getData());
1254 data.putString(KEY_MEDIA_SET_PATH, albumPath.toString());
1255 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString());
1256 mActivity.getStateManager().startState(SinglePhotoPage.class, data);
1259 mModel.setCurrentPhoto(path, mCurrentIndex);
1264 protected void onStateResult(int requestCode, int resultCode, Intent data) {
1265 if (resultCode == Activity.RESULT_CANCELED) {
1266 // This is a reset, not a canceled
1269 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
1270 // Unmap reset vs. canceled
1271 resultCode = Activity.RESULT_CANCELED;
1273 mRecenterCameraOnResume = false;
1274 switch (requestCode) {
1276 setCurrentPhotoByIntent(data);
1279 if (resultCode == Activity.RESULT_OK) {
1280 setCurrentPhotoByIntent(data);
1283 case REQUEST_CROP_PICASA: {
1284 if (resultCode == Activity.RESULT_OK) {
1285 Context context = mActivity.getAndroidContext();
1286 String message = context.getString(R.string.crop_saved,
1287 context.getString(R.string.folder_edited_online_photos));
1288 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
1292 case REQUEST_SLIDESHOW: {
1293 if (data == null) break;
1294 String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH);
1295 int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
1297 mModel.setCurrentPhoto(Path.fromString(path), index);
1304 public void onPause() {
1308 mActivity.getGLRoot().unfreeze();
1309 mHandler.removeMessages(MSG_UNFREEZE_GLROOT);
1311 DetailsHelper.pause();
1312 // Hide the detail dialog on exit
1313 if (mShowDetails) hideDetails();
1314 if (mModel != null) {
1318 mHandler.removeMessages(MSG_HIDE_BARS);
1319 mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS);
1320 refreshBottomControlsWhenReady();
1321 mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
1323 mActionBar.disableAlbumModeMenu(true);
1325 onCommitDeleteImage();
1326 mMenuExecutor.pause();
1327 if (mMediaSet != null) mMediaSet.clearDeletion();
1331 public void onCurrentImageUpdated() {
1332 mActivity.getGLRoot().unfreeze();
1336 public void onFilmModeChanged(boolean enabled) {
1337 refreshBottomControlsWhenReady();
1340 mActionBar.enableAlbumModeMenu(
1341 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this);
1343 mActionBar.disableAlbumModeMenu(true);
1347 mHandler.removeMessages(MSG_HIDE_BARS);
1349 refreshHidingMessage();
1353 private void transitionFromAlbumPageIfNeeded() {
1354 TransitionStore transitions = mActivity.getTransitionStore();
1356 int albumPageTransition = transitions.get(
1357 KEY_ALBUMPAGE_TRANSITION, MSG_ALBUMPAGE_NONE);
1359 if (albumPageTransition == MSG_ALBUMPAGE_NONE && mAppBridge != null
1360 && mRecenterCameraOnResume) {
1361 // Generally, resuming the PhotoPage when in Camera should
1362 // reset to the capture mode to allow quick photo taking
1364 mPhotoView.resetToFirstPicture();
1366 int resumeIndex = transitions.get(KEY_INDEX_HINT, -1);
1367 if (resumeIndex >= 0) {
1368 if (mHasCameraScreennailOrPlaceholder) {
1369 // Account for preview/placeholder being the first item
1372 if (resumeIndex < mMediaSet.getMediaItemCount()) {
1373 mCurrentIndex = resumeIndex;
1374 mModel.moveTo(mCurrentIndex);
1379 if (albumPageTransition == MSG_ALBUMPAGE_RESUMED) {
1380 mPhotoView.setFilmMode(mStartInFilmstrip || mAppBridge != null);
1381 } else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) {
1382 mPhotoView.setFilmMode(false);
1387 protected void onResume() {
1390 if (mModel == null) {
1391 mActivity.getStateManager().finishState(this);
1394 transitionFromAlbumPageIfNeeded();
1396 mActivity.getGLRoot().freeze();
1398 setContentPane(mRootPane);
1401 mPhotoView.resume();
1402 mActionBar.setDisplayOptions(
1403 ((mSecureAlbum == null) && (mSetPathString != null)), false);
1404 mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
1405 refreshBottomControlsWhenReady();
1406 if (mShowSpinner && mPhotoView.getFilmMode()) {
1407 mActionBar.enableAlbumModeMenu(
1408 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this);
1412 mActivity.getGLRoot().setLightsOutMode(true);
1414 boolean haveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*");
1415 if (haveImageEditor != mHaveImageEditor) {
1416 mHaveImageEditor = haveImageEditor;
1417 updateMenuOperations();
1420 mRecenterCameraOnResume = true;
1421 mHandler.sendEmptyMessageDelayed(MSG_UNFREEZE_GLROOT, UNFREEZE_GLROOT_TIMEOUT);
1425 protected void onDestroy() {
1426 if (mAppBridge != null) {
1427 mAppBridge.setServer(null);
1428 mScreenNailItem.setScreenNail(null);
1429 mAppBridge.detachScreenNail();
1431 mScreenNailSet = null;
1432 mScreenNailItem = null;
1434 mActivity.getGLRoot().setOrientationSource(null);
1435 if (mBottomControls != null) mBottomControls.cleanup();
1437 // Remove all pending messages.
1438 mHandler.removeCallbacksAndMessages(null);
1442 private class MyDetailsSource implements DetailsSource {
1445 public MediaDetails getDetails() {
1446 return mModel.getMediaItem(0).getDetails();
1451 return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
1455 public int setIndex() {
1456 return mModel.getCurrentIndex();
1461 public void onAlbumModeSelected(int mode) {
1462 if (mode == GalleryActionBar.ALBUM_GRID_MODE_SELECTED) {
1468 public void refreshBottomControlsWhenReady() {
1469 if (mBottomControls == null) {
1472 MediaObject currentPhoto = mCurrentPhoto;
1473 if (currentPhoto == null) {
1474 mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, 0, 0, currentPhoto).sendToTarget();
1476 currentPhoto.getPanoramaSupport(mRefreshBottomControlsCallback);
1480 private void updatePanoramaUI(boolean isPanorama360) {
1481 Menu menu = mActionBar.getMenu();
1483 // it could be null if onCreateActionBar has not been called yet
1488 MenuExecutor.updateMenuForPanorama(menu, isPanorama360, isPanorama360);
1490 if (isPanorama360) {
1491 MenuItem item = menu.findItem(R.id.action_share);
1493 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1494 item.setTitle(mActivity.getResources().getString(R.string.share_as_photo));
1496 } else if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_SHARE) != 0) {
1497 MenuItem item = menu.findItem(R.id.action_share);
1499 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
1500 item.setTitle(mActivity.getResources().getString(R.string.share));
1506 public void onUndoBarVisibilityChanged(boolean visible) {
1507 refreshBottomControlsWhenReady();