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