PhotoPage.java revision 372152961edd8feada0ae6478c91ba6b2565191d
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.gallery3d.app; 18 19import android.annotation.TargetApi; 20import android.app.Activity; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.graphics.Rect; 26import android.net.Uri; 27import android.nfc.NfcAdapter; 28import android.nfc.NfcAdapter.CreateBeamUrisCallback; 29import android.nfc.NfcEvent; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.Message; 33import android.os.SystemClock; 34import android.widget.RelativeLayout; 35import android.widget.Toast; 36 37import com.android.camera.CameraActivity; 38import com.android.camera.ProxyLauncher; 39 40import com.actionbarsherlock.app.ActionBar.OnMenuVisibilityListener; 41import com.actionbarsherlock.view.Menu; 42import com.actionbarsherlock.view.MenuItem; 43import com.android.gallery3d.R; 44import com.android.gallery3d.common.ApiHelper; 45import com.android.gallery3d.data.ComboAlbum; 46import com.android.gallery3d.data.DataManager; 47import com.android.gallery3d.data.FilterDeleteSet; 48import com.android.gallery3d.data.FilterSource; 49import com.android.gallery3d.data.LocalImage; 50import com.android.gallery3d.data.MediaDetails; 51import com.android.gallery3d.data.MediaItem; 52import com.android.gallery3d.data.MediaObject; 53import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; 54import com.android.gallery3d.data.MediaSet; 55import com.android.gallery3d.data.MtpSource; 56import com.android.gallery3d.data.Path; 57import com.android.gallery3d.data.SecureAlbum; 58import com.android.gallery3d.data.SecureSource; 59import com.android.gallery3d.data.SnailAlbum; 60import com.android.gallery3d.data.SnailItem; 61import com.android.gallery3d.data.SnailSource; 62import com.android.gallery3d.filtershow.FilterShowActivity; 63import com.android.gallery3d.picasasource.PicasaSource; 64import com.android.gallery3d.ui.DetailsHelper; 65import com.android.gallery3d.ui.DetailsHelper.CloseListener; 66import com.android.gallery3d.ui.DetailsHelper.DetailsSource; 67import com.android.gallery3d.ui.GLView; 68import com.android.gallery3d.ui.ImportCompleteListener; 69import com.android.gallery3d.ui.MenuExecutor; 70import com.android.gallery3d.ui.PhotoView; 71import com.android.gallery3d.ui.SelectionManager; 72import com.android.gallery3d.ui.SynchronizedHandler; 73import com.android.gallery3d.util.GalleryUtils; 74 75public class PhotoPage extends ActivityState implements 76 PhotoView.Listener, OrientationManager.Listener, AppBridge.Server, 77 PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener { 78 private static final String TAG = "PhotoPage"; 79 80 private static final int MSG_HIDE_BARS = 1; 81 private static final int MSG_ON_FULL_SCREEN_CHANGED = 4; 82 private static final int MSG_UPDATE_ACTION_BAR = 5; 83 private static final int MSG_UNFREEZE_GLROOT = 6; 84 private static final int MSG_WANT_BARS = 7; 85 private static final int MSG_REFRESH_BOTTOM_CONTROLS = 8; 86 private static final int MSG_ON_CAMERA_CENTER = 9; 87 private static final int MSG_ON_PICTURE_CENTER = 10; 88 private static final int MSG_REFRESH_IMAGE = 11; 89 private static final int MSG_UPDATE_PHOTO_UI = 12; 90 private static final int MSG_UPDATE_PROGRESS = 13; 91 private static final int MSG_UPDATE_DEFERRED = 14; 92 private static final int MSG_UPDATE_SHARE_URI = 15; 93 private static final int MSG_UPDATE_PANORAMA_UI = 16; 94 95 private static final int HIDE_BARS_TIMEOUT = 3500; 96 private static final int UNFREEZE_GLROOT_TIMEOUT = 250; 97 98 private static final int REQUEST_SLIDESHOW = 1; 99 private static final int REQUEST_CROP = 2; 100 private static final int REQUEST_CROP_PICASA = 3; 101 private static final int REQUEST_EDIT = 4; 102 private static final int REQUEST_PLAY_VIDEO = 5; 103 private static final int REQUEST_TRIM = 6; 104 105 public static final String KEY_MEDIA_SET_PATH = "media-set-path"; 106 public static final String KEY_MEDIA_ITEM_PATH = "media-item-path"; 107 public static final String KEY_INDEX_HINT = "index-hint"; 108 public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect"; 109 public static final String KEY_APP_BRIDGE = "app-bridge"; 110 public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up"; 111 public static final String KEY_START_IN_FILMSTRIP = "start-in-filmstrip"; 112 public static final String KEY_RETURN_INDEX_HINT = "return-index-hint"; 113 public static final String KEY_SHOW_WHEN_LOCKED = "show_when_locked"; 114 public static final String KEY_IN_CAMERA_ROLL = "in_camera_roll"; 115 116 public static final String KEY_ALBUMPAGE_TRANSITION = "albumpage-transition"; 117 public static final int MSG_ALBUMPAGE_NONE = 0; 118 public static final int MSG_ALBUMPAGE_STARTED = 1; 119 public static final int MSG_ALBUMPAGE_RESUMED = 2; 120 public static final int MSG_ALBUMPAGE_PICKED = 4; 121 122 public static final String ACTION_NEXTGEN_EDIT = "action_nextgen_edit"; 123 124 private GalleryApp mApplication; 125 private SelectionManager mSelectionManager; 126 127 private PhotoView mPhotoView; 128 private PhotoPage.Model mModel; 129 private DetailsHelper mDetailsHelper; 130 private boolean mShowDetails; 131 132 // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied. 133 // E.g., viewing a photo in gmail attachment 134 private FilterDeleteSet mMediaSet; 135 136 // The mediaset used by camera launched from secure lock screen. 137 private SecureAlbum mSecureAlbum; 138 139 private int mCurrentIndex = 0; 140 private Handler mHandler; 141 private boolean mShowBars = true; 142 private volatile boolean mActionBarAllowed = true; 143 private GalleryActionBar mActionBar; 144 private boolean mIsMenuVisible; 145 private boolean mHaveImageEditor; 146 private PhotoPageBottomControls mBottomControls; 147 private PhotoPageProgressBar mProgressBar; 148 private MediaItem mCurrentPhoto = null; 149 private MenuExecutor mMenuExecutor; 150 private boolean mIsActive; 151 private boolean mShowSpinner; 152 private String mSetPathString; 153 // This is the original mSetPathString before adding the camera preview item. 154 private String mOriginalSetPathString; 155 private AppBridge mAppBridge; 156 private SnailItem mScreenNailItem; 157 private SnailAlbum mScreenNailSet; 158 private OrientationManager mOrientationManager; 159 private boolean mTreatBackAsUp; 160 private boolean mStartInFilmstrip; 161 private boolean mHasCameraScreennailOrPlaceholder = false; 162 private boolean mRecenterCameraOnResume = true; 163 164 // These are only valid after the panorama callback 165 private boolean mIsPanorama; 166 private boolean mIsPanorama360; 167 168 private long mCameraSwitchCutoff = 0; 169 private boolean mSkipUpdateCurrentPhoto = false; 170 private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300; 171 172 private static final long DEFERRED_UPDATE_MS = 250; 173 private boolean mDeferredUpdateWaiting = false; 174 private long mDeferUpdateUntil = Long.MAX_VALUE; 175 176 // The item that is deleted (but it can still be undeleted before commiting) 177 private Path mDeletePath; 178 private boolean mDeleteIsFocus; // whether the deleted item was in focus 179 180 private Uri[] mNfcPushUris = new Uri[1]; 181 182 private final MyMenuVisibilityListener mMenuVisibilityListener = 183 new MyMenuVisibilityListener(); 184 private UpdateProgressListener mProgressListener; 185 186 private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() { 187 @Override 188 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama, 189 boolean isPanorama360) { 190 if (mediaObject == mCurrentPhoto) { 191 mHandler.obtainMessage(MSG_UPDATE_PANORAMA_UI, isPanorama360 ? 1 : 0, 0, 192 mediaObject).sendToTarget(); 193 } 194 } 195 }; 196 197 private final PanoramaSupportCallback mRefreshBottomControlsCallback = new PanoramaSupportCallback() { 198 @Override 199 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama, 200 boolean isPanorama360) { 201 if (mediaObject == mCurrentPhoto) { 202 mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, isPanorama ? 1 : 0, isPanorama360 ? 1 : 0, 203 mediaObject).sendToTarget(); 204 } 205 } 206 }; 207 208 private final PanoramaSupportCallback mUpdateShareURICallback = new PanoramaSupportCallback() { 209 @Override 210 public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama, 211 boolean isPanorama360) { 212 if (mediaObject == mCurrentPhoto) { 213 mHandler.obtainMessage(MSG_UPDATE_SHARE_URI, isPanorama360 ? 1 : 0, 0, mediaObject) 214 .sendToTarget(); 215 } 216 } 217 }; 218 219 public static interface Model extends PhotoView.Model { 220 public void resume(); 221 public void pause(); 222 public boolean isEmpty(); 223 public void setCurrentPhoto(Path path, int indexHint); 224 } 225 226 private class MyMenuVisibilityListener implements OnMenuVisibilityListener { 227 @Override 228 public void onMenuVisibilityChanged(boolean isVisible) { 229 mIsMenuVisible = isVisible; 230 refreshHidingMessage(); 231 } 232 } 233 234 private class UpdateProgressListener implements StitchingChangeListener { 235 236 @Override 237 public void onStitchingResult(Uri uri) { 238 sendUpdate(uri, MSG_REFRESH_IMAGE); 239 } 240 241 @Override 242 public void onStitchingQueued(Uri uri) { 243 sendUpdate(uri, MSG_UPDATE_PROGRESS); 244 } 245 246 @Override 247 public void onStitchingProgress(Uri uri, final int progress) { 248 sendUpdate(uri, MSG_UPDATE_PROGRESS); 249 } 250 251 private void sendUpdate(Uri uri, int message) { 252 MediaObject currentPhoto = mCurrentPhoto; 253 boolean isCurrentPhoto = currentPhoto instanceof LocalImage 254 && currentPhoto.getContentUri().equals(uri); 255 if (isCurrentPhoto) { 256 mHandler.sendEmptyMessage(message); 257 } 258 } 259 }; 260 261 @Override 262 protected int getBackgroundColorId() { 263 return R.color.photo_background; 264 } 265 266 private final GLView mRootPane = new GLView() { 267 @Override 268 protected void onLayout( 269 boolean changed, int left, int top, int right, int bottom) { 270 mPhotoView.layout(0, 0, right - left, bottom - top); 271 if (mShowDetails) { 272 mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom); 273 } 274 } 275 }; 276 277 @Override 278 public void onCreate(Bundle data, Bundle restoreState) { 279 super.onCreate(data, restoreState); 280 mActionBar = mActivity.getGalleryActionBar(); 281 mSelectionManager = new SelectionManager(mActivity, false); 282 mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager); 283 284 mPhotoView = new PhotoView(mActivity); 285 mPhotoView.setListener(this); 286 mRootPane.addComponent(mPhotoView); 287 mApplication = (GalleryApp) ((Activity) mActivity).getApplication(); 288 mOrientationManager = mActivity.getOrientationManager(); 289 mOrientationManager.addListener(this); 290 mActivity.getGLRoot().setOrientationSource(mOrientationManager); 291 292 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { 293 @Override 294 public void handleMessage(Message message) { 295 switch (message.what) { 296 case MSG_HIDE_BARS: { 297 hideBars(); 298 break; 299 } 300 case MSG_REFRESH_BOTTOM_CONTROLS: { 301 if (mCurrentPhoto == message.obj && mBottomControls != null) { 302 mIsPanorama = message.arg1 == 1; 303 mIsPanorama360 = message.arg2 == 1; 304 mBottomControls.refresh(); 305 } 306 break; 307 } 308 case MSG_ON_FULL_SCREEN_CHANGED: { 309 mAppBridge.onFullScreenChanged(message.arg1 == 1); 310 break; 311 } 312 case MSG_UPDATE_ACTION_BAR: { 313 updateBars(); 314 break; 315 } 316 case MSG_WANT_BARS: { 317 wantBars(); 318 break; 319 } 320 case MSG_UNFREEZE_GLROOT: { 321 mActivity.getGLRoot().unfreeze(); 322 break; 323 } 324 case MSG_UPDATE_DEFERRED: { 325 long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis(); 326 if (nextUpdate <= 0) { 327 mDeferredUpdateWaiting = false; 328 updateUIForCurrentPhoto(); 329 } else { 330 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate); 331 } 332 break; 333 } 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); 342 } else { 343 if (mAppBridge != null) mPhotoView.setFilmMode(false); 344 stayedOnCamera = true; 345 } 346 347 if (stayedOnCamera) { 348 if (mAppBridge == null) { 349 launchCamera(); 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 353 the camera app */ 354 mPhotoView.switchToImage(1); 355 } else { 356 updateBars(); 357 updateCurrentPhoto(mModel.getMediaItem(0)); 358 } 359 } 360 break; 361 } 362 case MSG_ON_PICTURE_CENTER: { 363 if (!mPhotoView.getFilmMode() && mCurrentPhoto != null 364 && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) { 365 mPhotoView.setFilmMode(true); 366 } 367 break; 368 } 369 case MSG_REFRESH_IMAGE: { 370 final MediaItem photo = mCurrentPhoto; 371 mCurrentPhoto = null; 372 updateCurrentPhoto(photo); 373 break; 374 } 375 case MSG_UPDATE_PHOTO_UI: { 376 updateUIForCurrentPhoto(); 377 break; 378 } 379 case MSG_UPDATE_PROGRESS: { 380 updateProgressBar(); 381 break; 382 } 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; 388 if (isPanorama360) { 389 panoramaIntent = createSharePanoramaIntent(contentUri); 390 } 391 Intent shareIntent = createShareIntent(mCurrentPhoto); 392 393 mActionBar.setShareIntents(panoramaIntent, shareIntent); 394 setNfcBeamPushUri(contentUri); 395 } 396 break; 397 } 398 case MSG_UPDATE_PANORAMA_UI: { 399 if (mCurrentPhoto == message.obj) { 400 boolean isPanorama360 = message.arg1 != 0; 401 updatePanoramaUI(isPanorama360); 402 } 403 break; 404 } 405 default: throw new AssertionError(message.what); 406 } 407 } 408 }; 409 410 mSetPathString = data.getString(KEY_MEDIA_SET_PATH); 411 mOriginalSetPathString = mSetPathString; 412 setupNfcBeamPush(); 413 String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH); 414 Path itemPath = itemPathString != null ? 415 Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)) : 416 null; 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) { 422 mShowSpinner = true; 423 mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE); 424 if (mAppBridge != null) { 425 mShowBars = false; 426 mHasCameraScreennailOrPlaceholder = true; 427 mAppBridge.setServer(this); 428 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()); 438 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; 442 } 443 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; 451 } 452 mSetPathString = "/filter/empty/{"+mSetPathString+"}"; 453 } 454 455 // Combine the original MediaSet with the one for ScreenNail 456 // from AppBridge. 457 mSetPathString = "/combo/item/{" + screenNailSetPath + 458 "," + mSetPathString + "}"; 459 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 + "}"; 465 mCurrentIndex++; 466 mHasCameraScreennailOrPlaceholder = true; 467 } 468 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); 475 } 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); 482 } 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) 488 .get(0).getPath(); 489 } else { 490 // Bail out, PhotoPage can't load on an empty album 491 return; 492 } 493 } 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()); 499 mModel = pda; 500 mPhotoView.setModel(mModel); 501 502 pda.setDataListener(new PhotoDataAdapter.DataListener() { 503 504 @Override 505 public void onPhotoChanged(int index, Path item) { 506 int oldIndex = mCurrentIndex; 507 mCurrentIndex = index; 508 509 if (mHasCameraScreennailOrPlaceholder) { 510 if (mCurrentIndex > 0) { 511 mSkipUpdateCurrentPhoto = false; 512 } 513 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; 524 } 525 } 526 if (!mSkipUpdateCurrentPhoto) { 527 if (item != null) { 528 MediaItem photo = mModel.getMediaItem(0); 529 if (photo != null) updateCurrentPhoto(photo); 530 } 531 updateBars(); 532 } 533 // Reset the timeout for the bars after a swipe 534 refreshHidingMessage(); 535 } 536 537 @Override 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( 547 PhotoPage.this); 548 } 549 } 550 } 551 552 @Override 553 public void onLoadingStarted() { 554 } 555 }); 556 } else { 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; 564 } 565 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); 572 } 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); 580 } 581 } 582 } 583 } 584 585 @Override 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); 592 } 593 594 @Override 595 public boolean canDisplayBottomControls() { 596 return mIsActive && !mPhotoView.canUndo(); 597 } 598 599 @Override 600 public boolean canDisplayBottomControl(int control) { 601 if (mCurrentPhoto == null) { 602 return false; 603 } 604 switch(control) { 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: 611 return mIsPanorama; 612 case R.id.photopage_bottom_control_tiny_planet: 613 return mHaveImageEditor && mShowBars 614 && mIsPanorama360 && !mPhotoView.getFilmMode(); 615 default: 616 return false; 617 } 618 } 619 620 @Override 621 public void onBottomControlClicked(int control) { 622 switch(control) { 623 case R.id.photopage_bottom_control_edit: 624 launchPhotoEditor(); 625 return; 626 case R.id.photopage_bottom_control_panorama: 627 mActivity.getPanoramaViewHelper() 628 .showPanorama(mCurrentPhoto.getContentUri()); 629 return; 630 case R.id.photopage_bottom_control_tiny_planet: 631 launchTinyPlanet(); 632 return; 633 default: 634 return; 635 } 636 } 637 638 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 639 private void setupNfcBeamPush() { 640 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return; 641 642 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mActivity); 643 if (adapter != null) { 644 adapter.setBeamPushUris(null, mActivity); 645 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { 646 @Override 647 public Uri[] createBeamUris(NfcEvent event) { 648 return mNfcPushUris; 649 } 650 }, mActivity); 651 } 652 } 653 654 private void setNfcBeamPushUri(Uri uri) { 655 mNfcPushUris[0] = uri; 656 } 657 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); 664 } 665 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); 671 } 672 673 private void overrideTransitionToEditor() { 674 ((Activity) mActivity).overridePendingTransition(android.R.anim.slide_in_left, 675 android.R.anim.fade_out); 676 } 677 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(); 689 } 690 691 private void launchCamera() { 692 Intent intent = new Intent(mActivity, CameraActivity.class); 693 mRecenterCameraOnResume = false; 694 mActivity.startActivity(intent); 695 } 696 697 private void launchPhotoEditor() { 698 MediaItem current = mModel.getMediaItem(0); 699 if (current == null || (current.getSupportedOperations() 700 & MediaObject.SUPPORT_EDIT) == 0) { 701 return; 702 } 703 704 Intent intent = new Intent(ACTION_NEXTGEN_EDIT); 705 706 intent.setDataAndType(current.getContentUri(), current.getMimeType()) 707 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 708 if (mActivity.getPackageManager() 709 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) { 710 intent.setAction(Intent.ACTION_EDIT); 711 } 712 intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, 713 mActivity.isFullscreen()); 714 ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null), 715 REQUEST_EDIT); 716 overrideTransitionToEditor(); 717 } 718 719 private void requestDeferredUpdate() { 720 mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS; 721 if (!mDeferredUpdateWaiting) { 722 mDeferredUpdateWaiting = true; 723 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS); 724 } 725 } 726 727 private void updateUIForCurrentPhoto() { 728 if (mCurrentPhoto == null) return; 729 730 // If by swiping or deletion the user ends up on an action item 731 // and zoomed in, zoom out so that the context of the action is 732 // more clear 733 if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0 734 && !mPhotoView.getFilmMode()) { 735 mPhotoView.setWantPictureCenterCallbacks(true); 736 } 737 738 updateMenuOperations(); 739 refreshBottomControlsWhenReady(); 740 if (mShowDetails) { 741 mDetailsHelper.reloadDetails(); 742 } 743 if ((mSecureAlbum == null) 744 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { 745 mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback); 746 } 747 updateProgressBar(); 748 } 749 750 private void updateCurrentPhoto(MediaItem photo) { 751 if (mCurrentPhoto == photo) return; 752 mCurrentPhoto = photo; 753 if (mPhotoView.getFilmMode()) { 754 requestDeferredUpdate(); 755 } else { 756 updateUIForCurrentPhoto(); 757 } 758 } 759 760 private void updateProgressBar() { 761 if (mProgressBar != null) { 762 mProgressBar.hideProgress(); 763 StitchingProgressManager progressManager = mApplication.getStitchingProgressManager(); 764 if (progressManager != null && mCurrentPhoto instanceof LocalImage) { 765 Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri()); 766 if (progress != null) { 767 mProgressBar.setProgress(progress); 768 } 769 } 770 } 771 } 772 773 private void updateMenuOperations() { 774 Menu menu = mActionBar.getMenu(); 775 776 // it could be null if onCreateActionBar has not been called yet 777 if (menu == null) return; 778 779 MenuItem item = menu.findItem(R.id.action_slideshow); 780 if (item != null) { 781 item.setVisible((mSecureAlbum == null) && canDoSlideShow()); 782 } 783 if (mCurrentPhoto == null) return; 784 785 int supportedOperations = mCurrentPhoto.getSupportedOperations(); 786 if (mSecureAlbum != null) { 787 supportedOperations &= MediaObject.SUPPORT_DELETE; 788 } else if (!mHaveImageEditor) { 789 supportedOperations &= ~MediaObject.SUPPORT_EDIT; 790 } 791 MenuExecutor.updateMenuOperation(menu, supportedOperations); 792 mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback); 793 } 794 795 private boolean canDoSlideShow() { 796 if (mMediaSet == null || mCurrentPhoto == null) { 797 return false; 798 } 799 if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) { 800 return false; 801 } 802 if (MtpSource.isMtpPath(mOriginalSetPathString)) { 803 return false; 804 } 805 return true; 806 } 807 808 ////////////////////////////////////////////////////////////////////////// 809 // Action Bar show/hide management 810 ////////////////////////////////////////////////////////////////////////// 811 812 private void showBars() { 813 if (mShowBars) return; 814 mShowBars = true; 815 mOrientationManager.unlockOrientation(); 816 mActionBar.show(); 817 mActivity.getGLRoot().setLightsOutMode(false); 818 refreshHidingMessage(); 819 refreshBottomControlsWhenReady(); 820 } 821 822 private void hideBars() { 823 if (!mShowBars) return; 824 mShowBars = false; 825 mActionBar.hide(); 826 mActivity.getGLRoot().setLightsOutMode(true); 827 mHandler.removeMessages(MSG_HIDE_BARS); 828 refreshBottomControlsWhenReady(); 829 } 830 831 private void refreshHidingMessage() { 832 mHandler.removeMessages(MSG_HIDE_BARS); 833 if (!mIsMenuVisible && !mPhotoView.getFilmMode()) { 834 mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT); 835 } 836 } 837 838 private boolean canShowBars() { 839 // No bars if we are showing camera preview. 840 if (mAppBridge != null && mCurrentIndex == 0 841 && !mPhotoView.getFilmMode()) return false; 842 843 // No bars if it's not allowed. 844 if (!mActionBarAllowed) return false; 845 846 return true; 847 } 848 849 private void wantBars() { 850 if (canShowBars()) showBars(); 851 } 852 853 private void toggleBars() { 854 if (mShowBars) { 855 hideBars(); 856 } else { 857 if (canShowBars()) showBars(); 858 } 859 } 860 861 private void updateBars() { 862 if (!canShowBars()) { 863 hideBars(); 864 } 865 } 866 867 @Override 868 public void onOrientationCompensationChanged() { 869 mActivity.getGLRoot().requestLayoutContentPane(); 870 } 871 872 @Override 873 protected void onBackPressed() { 874 if (mShowDetails) { 875 hideDetails(); 876 } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) { 877 // We are leaving this page. Set the result now. 878 setResult(); 879 if (mStartInFilmstrip && !mPhotoView.getFilmMode()) { 880 mPhotoView.setFilmMode(true); 881 } else if (mTreatBackAsUp) { 882 onUpPressed(); 883 } else { 884 super.onBackPressed(); 885 } 886 } 887 } 888 889 private void onUpPressed() { 890 if ((mStartInFilmstrip || mAppBridge != null) 891 && !mPhotoView.getFilmMode()) { 892 mPhotoView.setFilmMode(true); 893 return; 894 } 895 896 if (mActivity.getStateManager().getStateCount() > 1) { 897 setResult(); 898 super.onBackPressed(); 899 return; 900 } 901 902 if (mOriginalSetPathString == null) return; 903 904 if (mAppBridge == null) { 905 // We're in view mode so set up the stacks on our own. 906 Bundle data = new Bundle(getData()); 907 data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString); 908 data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH, 909 mActivity.getDataManager().getTopSetPath( 910 DataManager.INCLUDE_ALL)); 911 mActivity.getStateManager().switchState(this, AlbumPage.class, data); 912 } else { 913 GalleryUtils.startGalleryActivity(mActivity); 914 } 915 } 916 917 private void setResult() { 918 Intent result = null; 919 result = new Intent(); 920 result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex); 921 setStateResult(Activity.RESULT_OK, result); 922 } 923 924 ////////////////////////////////////////////////////////////////////////// 925 // AppBridge.Server interface 926 ////////////////////////////////////////////////////////////////////////// 927 928 @Override 929 public void setCameraRelativeFrame(Rect frame) { 930 mPhotoView.setCameraRelativeFrame(frame); 931 } 932 933 @Override 934 public boolean switchWithCaptureAnimation(int offset) { 935 return mPhotoView.switchWithCaptureAnimation(offset); 936 } 937 938 @Override 939 public void setSwipingEnabled(boolean enabled) { 940 mPhotoView.setSwipingEnabled(enabled); 941 } 942 943 @Override 944 public void notifyScreenNailChanged() { 945 mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail()); 946 mScreenNailSet.notifyChange(); 947 } 948 949 @Override 950 public void addSecureAlbumItem(boolean isVideo, int id) { 951 mSecureAlbum.addMediaItem(isVideo, id); 952 } 953 954 @Override 955 protected boolean onCreateActionBar(Menu menu) { 956 mActionBar.createActionBarMenu(R.menu.photo, menu); 957 mHaveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*"); 958 updateMenuOperations(); 959 mActionBar.setTitle(mMediaSet != null ? mMediaSet.getName() : ""); 960 return true; 961 } 962 963 private MenuExecutor.ProgressListener mConfirmDialogListener = 964 new MenuExecutor.ProgressListener() { 965 @Override 966 public void onProgressUpdate(int index) {} 967 968 @Override 969 public void onProgressComplete(int result) {} 970 971 @Override 972 public void onConfirmDialogShown() { 973 mHandler.removeMessages(MSG_HIDE_BARS); 974 } 975 976 @Override 977 public void onConfirmDialogDismissed(boolean confirmed) { 978 refreshHidingMessage(); 979 } 980 981 @Override 982 public void onProgressStart() {} 983 }; 984 985 private void switchToGrid() { 986 if (mActivity.getStateManager().hasStateClass(AlbumPage.class)) { 987 onUpPressed(); 988 } else { 989 if (mOriginalSetPathString == null) return; 990 if (mProgressBar != null) { 991 updateCurrentPhoto(null); 992 mProgressBar.hideProgress(); 993 } 994 Bundle data = new Bundle(getData()); 995 data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString); 996 data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH, 997 mActivity.getDataManager().getTopSetPath( 998 DataManager.INCLUDE_ALL)); 999 1000 // We only show cluster menu in the first AlbumPage in stack 1001 // TODO: Enable this when running from the camera app 1002 boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class); 1003 data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum 1004 && mAppBridge == null); 1005 1006 data.putBoolean(PhotoPage.KEY_APP_BRIDGE, mAppBridge != null); 1007 1008 // Account for live preview being first item 1009 mActivity.getTransitionStore().put(KEY_RETURN_INDEX_HINT, 1010 mAppBridge != null ? mCurrentIndex - 1 : mCurrentIndex); 1011 1012 if (mHasCameraScreennailOrPlaceholder && mAppBridge != null) { 1013 mActivity.getStateManager().startState(AlbumPage.class, data); 1014 } else { 1015 mActivity.getStateManager().switchState(this, AlbumPage.class, data); 1016 } 1017 } 1018 } 1019 1020 @Override 1021 protected boolean onItemSelected(MenuItem item) { 1022 if (mModel == null) return true; 1023 refreshHidingMessage(); 1024 MediaItem current = mModel.getMediaItem(0); 1025 1026 if (current == null) { 1027 // item is not ready, ignore 1028 return true; 1029 } 1030 1031 int currentIndex = mModel.getCurrentIndex(); 1032 Path path = current.getPath(); 1033 1034 DataManager manager = mActivity.getDataManager(); 1035 int action = item.getItemId(); 1036 String confirmMsg = null; 1037 switch (action) { 1038 case android.R.id.home: { 1039 onUpPressed(); 1040 return true; 1041 } 1042 case R.id.action_slideshow: { 1043 Bundle data = new Bundle(); 1044 data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString()); 1045 data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString()); 1046 data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex); 1047 data.putBoolean(SlideshowPage.KEY_REPEAT, true); 1048 mActivity.getStateManager().startStateForResult( 1049 SlideshowPage.class, REQUEST_SLIDESHOW, data); 1050 return true; 1051 } 1052 case R.id.action_crop: { 1053 Activity activity = mActivity; 1054 Intent intent = new Intent(FilterShowActivity.CROP_ACTION); 1055 intent.setClass(activity, FilterShowActivity.class); 1056 intent.setDataAndType(manager.getContentUri(path), current.getMimeType()) 1057 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1058 activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current) 1059 ? REQUEST_CROP_PICASA 1060 : REQUEST_CROP); 1061 return true; 1062 } 1063 case R.id.action_trim: { 1064 Intent intent = new Intent(mActivity, TrimVideo.class); 1065 intent.setData(manager.getContentUri(path)); 1066 // We need the file path to wrap this into a RandomAccessFile. 1067 intent.putExtra(KEY_MEDIA_ITEM_PATH, current.getFilePath()); 1068 mActivity.startActivityForResult(intent, REQUEST_TRIM); 1069 return true; 1070 } 1071 case R.id.action_edit: { 1072 launchPhotoEditor(); 1073 return true; 1074 } 1075 case R.id.action_details: { 1076 if (mShowDetails) { 1077 hideDetails(); 1078 } else { 1079 showDetails(); 1080 } 1081 return true; 1082 } 1083 case R.id.action_delete: 1084 confirmMsg = mActivity.getResources().getQuantityString( 1085 R.plurals.delete_selection, 1); 1086 case R.id.action_setas: 1087 case R.id.action_rotate_ccw: 1088 case R.id.action_rotate_cw: 1089 case R.id.action_show_on_map: 1090 mSelectionManager.deSelectAll(); 1091 mSelectionManager.toggle(path); 1092 mMenuExecutor.onMenuClicked(item, confirmMsg, mConfirmDialogListener); 1093 return true; 1094 case R.id.action_import: 1095 mSelectionManager.deSelectAll(); 1096 mSelectionManager.toggle(path); 1097 mMenuExecutor.onMenuClicked(item, confirmMsg, 1098 new ImportCompleteListener(mActivity)); 1099 return true; 1100 default : 1101 return false; 1102 } 1103 } 1104 1105 private void hideDetails() { 1106 mShowDetails = false; 1107 mDetailsHelper.hide(); 1108 } 1109 1110 private void showDetails() { 1111 mShowDetails = true; 1112 if (mDetailsHelper == null) { 1113 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource()); 1114 mDetailsHelper.setCloseListener(new CloseListener() { 1115 @Override 1116 public void onClose() { 1117 hideDetails(); 1118 } 1119 }); 1120 } 1121 mDetailsHelper.show(); 1122 } 1123 1124 //////////////////////////////////////////////////////////////////////////// 1125 // Callbacks from PhotoView 1126 //////////////////////////////////////////////////////////////////////////// 1127 @Override 1128 public void onSingleTapUp(int x, int y) { 1129 if (mAppBridge != null) { 1130 if (mAppBridge.onSingleTapUp(x, y)) return; 1131 } 1132 1133 MediaItem item = mModel.getMediaItem(0); 1134 if (item == null || item == mScreenNailItem) { 1135 // item is not ready or it is camera preview, ignore 1136 return; 1137 } 1138 1139 int supported = item.getSupportedOperations(); 1140 boolean playVideo = (mSecureAlbum == null) && 1141 ((supported & MediaItem.SUPPORT_PLAY) != 0); 1142 boolean unlock = ((supported & MediaItem.SUPPORT_UNLOCK) != 0); 1143 boolean goBack = ((supported & MediaItem.SUPPORT_BACK) != 0); 1144 boolean launchCamera = ((supported & MediaItem.SUPPORT_CAMERA_SHORTCUT) != 0); 1145 1146 if (playVideo) { 1147 // determine if the point is at center (1/6) of the photo view. 1148 // (The position of the "play" icon is at center (1/6) of the photo) 1149 int w = mPhotoView.getWidth(); 1150 int h = mPhotoView.getHeight(); 1151 playVideo = (Math.abs(x - w / 2) * 12 <= w) 1152 && (Math.abs(y - h / 2) * 12 <= h); 1153 } 1154 1155 if (playVideo) { 1156 playVideo(mActivity, item.getPlayUri(), item.getName()); 1157 } else if (goBack) { 1158 onBackPressed(); 1159 } else if (unlock) { 1160 mActivity.getStateManager().finishState(this); 1161 } else if (launchCamera) { 1162 launchCamera(); 1163 } else { 1164 toggleBars(); 1165 } 1166 } 1167 1168 @Override 1169 public void onActionBarAllowed(boolean allowed) { 1170 mActionBarAllowed = allowed; 1171 mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR); 1172 } 1173 1174 @Override 1175 public void onActionBarWanted() { 1176 mHandler.sendEmptyMessage(MSG_WANT_BARS); 1177 } 1178 1179 @Override 1180 public void onFullScreenChanged(boolean full) { 1181 Message m = mHandler.obtainMessage( 1182 MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0); 1183 m.sendToTarget(); 1184 } 1185 1186 // How we do delete/undo: 1187 // 1188 // When the user choose to delete a media item, we just tell the 1189 // FilterDeleteSet to hide that item. If the user choose to undo it, we 1190 // again tell FilterDeleteSet not to hide it. If the user choose to commit 1191 // the deletion, we then actually delete the media item. 1192 @Override 1193 public void onDeleteImage(Path path, int offset) { 1194 onCommitDeleteImage(); // commit the previous deletion 1195 mDeletePath = path; 1196 mDeleteIsFocus = (offset == 0); 1197 mMediaSet.addDeletion(path, mCurrentIndex + offset); 1198 } 1199 1200 @Override 1201 public void onUndoDeleteImage() { 1202 if (mDeletePath == null) return; 1203 // If the deletion was done on the focused item, we want the model to 1204 // focus on it when it is undeleted. 1205 if (mDeleteIsFocus) mModel.setFocusHintPath(mDeletePath); 1206 mMediaSet.removeDeletion(mDeletePath); 1207 mDeletePath = null; 1208 } 1209 1210 @Override 1211 public void onCommitDeleteImage() { 1212 if (mDeletePath == null) return; 1213 mSelectionManager.deSelectAll(); 1214 mSelectionManager.toggle(mDeletePath); 1215 mMenuExecutor.onMenuClicked(R.id.action_delete, null, true, false); 1216 mDeletePath = null; 1217 } 1218 1219 public void playVideo(Activity activity, Uri uri, String title) { 1220 try { 1221 Intent intent = new Intent(Intent.ACTION_VIEW) 1222 .setDataAndType(uri, "video/*") 1223 .putExtra(Intent.EXTRA_TITLE, title) 1224 .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true); 1225 activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO); 1226 } catch (ActivityNotFoundException e) { 1227 Toast.makeText(activity, activity.getString(R.string.video_err), 1228 Toast.LENGTH_SHORT).show(); 1229 } 1230 } 1231 1232 private void setCurrentPhotoByIntent(Intent intent) { 1233 if (intent == null) return; 1234 Path path = mApplication.getDataManager() 1235 .findPathByUri(intent.getData(), intent.getType()); 1236 if (path != null) { 1237 Path albumPath = mApplication.getDataManager().getDefaultSetOf(path); 1238 if (!albumPath.equalsIgnoreCase(mOriginalSetPathString)) { 1239 // If the edited image is stored in a different album, we need 1240 // to start a new activity state to show the new image 1241 Bundle data = new Bundle(getData()); 1242 data.putString(KEY_MEDIA_SET_PATH, albumPath.toString()); 1243 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString()); 1244 mActivity.getStateManager().startState(PhotoPage.class, data); 1245 return; 1246 } 1247 mModel.setCurrentPhoto(path, mCurrentIndex); 1248 } 1249 } 1250 1251 @Override 1252 protected void onStateResult(int requestCode, int resultCode, Intent data) { 1253 if (resultCode == Activity.RESULT_CANCELED) { 1254 // This is a reset, not a canceled 1255 return; 1256 } 1257 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 1258 // Unmap reset vs. canceled 1259 resultCode = Activity.RESULT_CANCELED; 1260 } 1261 mRecenterCameraOnResume = false; 1262 switch (requestCode) { 1263 case REQUEST_EDIT: 1264 setCurrentPhotoByIntent(data); 1265 break; 1266 case REQUEST_CROP: 1267 if (resultCode == Activity.RESULT_OK) { 1268 setCurrentPhotoByIntent(data); 1269 } 1270 break; 1271 case REQUEST_CROP_PICASA: { 1272 if (resultCode == Activity.RESULT_OK) { 1273 Context context = mActivity.getAndroidContext(); 1274 String message = context.getString(R.string.crop_saved, 1275 context.getString(R.string.folder_edited_online_photos)); 1276 Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); 1277 } 1278 break; 1279 } 1280 case REQUEST_SLIDESHOW: { 1281 if (data == null) break; 1282 String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH); 1283 int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0); 1284 if (path != null) { 1285 mModel.setCurrentPhoto(Path.fromString(path), index); 1286 } 1287 } 1288 } 1289 } 1290 1291 @Override 1292 public void onPause() { 1293 super.onPause(); 1294 mIsActive = false; 1295 1296 mActivity.getGLRoot().unfreeze(); 1297 mHandler.removeMessages(MSG_UNFREEZE_GLROOT); 1298 1299 DetailsHelper.pause(); 1300 // Hide the detail dialog on exit 1301 if (mShowDetails) hideDetails(); 1302 if (mModel != null) { 1303 mModel.pause(); 1304 } 1305 mPhotoView.pause(); 1306 mHandler.removeMessages(MSG_HIDE_BARS); 1307 mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS); 1308 refreshBottomControlsWhenReady(); 1309 mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener); 1310 if (mShowSpinner) { 1311 mActionBar.disableAlbumModeMenu(true); 1312 } 1313 onCommitDeleteImage(); 1314 mMenuExecutor.pause(); 1315 if (mMediaSet != null) mMediaSet.clearDeletion(); 1316 } 1317 1318 @Override 1319 public void onCurrentImageUpdated() { 1320 mActivity.getGLRoot().unfreeze(); 1321 } 1322 1323 @Override 1324 public void onFilmModeChanged(boolean enabled) { 1325 refreshBottomControlsWhenReady(); 1326 if (mShowSpinner) { 1327 if (enabled) { 1328 mActionBar.enableAlbumModeMenu( 1329 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); 1330 } else { 1331 mActionBar.disableAlbumModeMenu(true); 1332 } 1333 } 1334 if (enabled) { 1335 mHandler.removeMessages(MSG_HIDE_BARS); 1336 } else { 1337 refreshHidingMessage(); 1338 } 1339 } 1340 1341 private void transitionFromAlbumPageIfNeeded() { 1342 TransitionStore transitions = mActivity.getTransitionStore(); 1343 1344 int albumPageTransition = transitions.get( 1345 KEY_ALBUMPAGE_TRANSITION, MSG_ALBUMPAGE_NONE); 1346 1347 if (albumPageTransition == MSG_ALBUMPAGE_NONE && mAppBridge != null 1348 && mRecenterCameraOnResume) { 1349 // Generally, resuming the PhotoPage when in Camera should 1350 // reset to the capture mode to allow quick photo taking 1351 mCurrentIndex = 0; 1352 mPhotoView.resetToFirstPicture(); 1353 } else { 1354 int resumeIndex = transitions.get(KEY_INDEX_HINT, -1); 1355 if (resumeIndex >= 0) { 1356 if (mHasCameraScreennailOrPlaceholder) { 1357 // Account for preview/placeholder being the first item 1358 resumeIndex++; 1359 } 1360 if (resumeIndex < mMediaSet.getMediaItemCount()) { 1361 mCurrentIndex = resumeIndex; 1362 mModel.moveTo(mCurrentIndex); 1363 } 1364 } 1365 } 1366 1367 if (albumPageTransition == MSG_ALBUMPAGE_RESUMED) { 1368 mPhotoView.setFilmMode(mStartInFilmstrip || mAppBridge != null); 1369 } else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) { 1370 mPhotoView.setFilmMode(false); 1371 } 1372 } 1373 1374 @Override 1375 protected void onResume() { 1376 super.onResume(); 1377 1378 if (mModel == null) { 1379 mActivity.getStateManager().finishState(this); 1380 return; 1381 } 1382 transitionFromAlbumPageIfNeeded(); 1383 1384 mActivity.getGLRoot().freeze(); 1385 mIsActive = true; 1386 setContentPane(mRootPane); 1387 1388 mModel.resume(); 1389 mPhotoView.resume(); 1390 mActionBar.setDisplayOptions( 1391 ((mSecureAlbum == null) && (mSetPathString != null)), false); 1392 mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener); 1393 refreshBottomControlsWhenReady(); 1394 if (mShowSpinner && mPhotoView.getFilmMode()) { 1395 mActionBar.enableAlbumModeMenu( 1396 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); 1397 } 1398 if (!mShowBars) { 1399 mActionBar.hide(); 1400 mActivity.getGLRoot().setLightsOutMode(true); 1401 } 1402 boolean haveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*"); 1403 if (haveImageEditor != mHaveImageEditor) { 1404 mHaveImageEditor = haveImageEditor; 1405 updateMenuOperations(); 1406 } 1407 1408 mRecenterCameraOnResume = true; 1409 mHandler.sendEmptyMessageDelayed(MSG_UNFREEZE_GLROOT, UNFREEZE_GLROOT_TIMEOUT); 1410 } 1411 1412 @Override 1413 protected void onDestroy() { 1414 if (mAppBridge != null) { 1415 mAppBridge.setServer(null); 1416 mScreenNailItem.setScreenNail(null); 1417 mAppBridge.detachScreenNail(); 1418 mAppBridge = null; 1419 mScreenNailSet = null; 1420 mScreenNailItem = null; 1421 } 1422 mOrientationManager.removeListener(this); 1423 mActivity.getGLRoot().setOrientationSource(null); 1424 if (mBottomControls != null) mBottomControls.cleanup(); 1425 1426 // Remove all pending messages. 1427 mHandler.removeCallbacksAndMessages(null); 1428 super.onDestroy(); 1429 } 1430 1431 private class MyDetailsSource implements DetailsSource { 1432 1433 @Override 1434 public MediaDetails getDetails() { 1435 return mModel.getMediaItem(0).getDetails(); 1436 } 1437 1438 @Override 1439 public int size() { 1440 return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1; 1441 } 1442 1443 @Override 1444 public int setIndex() { 1445 return mModel.getCurrentIndex(); 1446 } 1447 } 1448 1449 @Override 1450 public void onAlbumModeSelected(int mode) { 1451 if (mode == GalleryActionBar.ALBUM_GRID_MODE_SELECTED) { 1452 switchToGrid(); 1453 } 1454 } 1455 1456 @Override 1457 public void refreshBottomControlsWhenReady() { 1458 if (mBottomControls == null) { 1459 return; 1460 } 1461 MediaObject currentPhoto = mCurrentPhoto; 1462 if (currentPhoto == null) { 1463 mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, 0, 0, currentPhoto).sendToTarget(); 1464 } else { 1465 currentPhoto.getPanoramaSupport(mRefreshBottomControlsCallback); 1466 } 1467 } 1468 1469 private void updatePanoramaUI(boolean isPanorama360) { 1470 Menu menu = mActionBar.getMenu(); 1471 1472 // it could be null if onCreateActionBar has not been called yet 1473 if (menu == null) { 1474 return; 1475 } 1476 1477 MenuExecutor.updateMenuForPanorama(menu, isPanorama360, isPanorama360); 1478 1479 if (isPanorama360) { 1480 MenuItem item = menu.findItem(R.id.action_share); 1481 if (item != null) { 1482 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1483 item.setTitle(mActivity.getResources().getString(R.string.share_as_photo)); 1484 } 1485 } else if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_SHARE) != 0) { 1486 MenuItem item = menu.findItem(R.id.action_share); 1487 if (item != null) { 1488 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1489 item.setTitle(mActivity.getResources().getString(R.string.share)); 1490 } 1491 } 1492 } 1493 1494 @Override 1495 public void onUndoBarVisibilityChanged(boolean visible) { 1496 refreshBottomControlsWhenReady(); 1497 } 1498} 1499