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