PhotoPage.java revision 1a719804ddd5c6fc9a6914ef5a2b6e914834d1f9
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.content.res.Configuration; 27import android.graphics.Rect; 28import android.net.Uri; 29import android.nfc.NfcAdapter; 30import android.nfc.NfcAdapter.CreateBeamUrisCallback; 31import android.nfc.NfcEvent; 32import android.os.Bundle; 33import android.os.Handler; 34import android.os.Message; 35import android.os.SystemClock; 36import android.view.Menu; 37import android.view.MenuItem; 38import android.widget.RelativeLayout; 39import android.widget.Toast; 40 41import com.android.camera.CameraActivity; 42import com.android.camera.ProxyLauncher; 43import com.android.gallery3d.R; 44import com.android.gallery3d.common.ApiHelper; 45import com.android.gallery3d.data.ComboAlbum; 46import com.android.gallery3d.data.DataManager; 47import com.android.gallery3d.data.FilterDeleteSet; 48import com.android.gallery3d.data.FilterSource; 49import com.android.gallery3d.data.LocalImage; 50import com.android.gallery3d.data.MediaDetails; 51import com.android.gallery3d.data.MediaItem; 52import com.android.gallery3d.data.MediaObject; 53import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; 54import com.android.gallery3d.data.MediaSet; 55import com.android.gallery3d.data.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.MenuExecutor; 68import com.android.gallery3d.ui.PhotoView; 69import com.android.gallery3d.ui.SelectionManager; 70import com.android.gallery3d.ui.SynchronizedHandler; 71import com.android.gallery3d.util.GalleryUtils; 72import com.android.gallery3d.util.UsageStatistics; 73 74public abstract class PhotoPage extends ActivityState implements 75 PhotoView.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 mActivity.getGLRoot().setOrientationSource(mOrientationManager); 289 290 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { 291 @Override 292 public void handleMessage(Message message) { 293 switch (message.what) { 294 case MSG_HIDE_BARS: { 295 hideBars(); 296 break; 297 } 298 case MSG_REFRESH_BOTTOM_CONTROLS: { 299 if (mCurrentPhoto == message.obj && mBottomControls != null) { 300 mIsPanorama = message.arg1 == 1; 301 mIsPanorama360 = message.arg2 == 1; 302 mBottomControls.refresh(); 303 } 304 break; 305 } 306 case MSG_ON_FULL_SCREEN_CHANGED: { 307 if (mAppBridge != null) { 308 mAppBridge.onFullScreenChanged(message.arg1 == 1); 309 } 310 break; 311 } 312 case MSG_UPDATE_ACTION_BAR: { 313 updateBars(); 314 break; 315 } 316 case MSG_WANT_BARS: { 317 wantBars(); 318 break; 319 } 320 case MSG_UNFREEZE_GLROOT: { 321 mActivity.getGLRoot().unfreeze(); 322 break; 323 } 324 case MSG_UPDATE_DEFERRED: { 325 long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis(); 326 if (nextUpdate <= 0) { 327 mDeferredUpdateWaiting = false; 328 updateUIForCurrentPhoto(); 329 } else { 330 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate); 331 } 332 break; 333 } 334 case MSG_ON_CAMERA_CENTER: { 335 mSkipUpdateCurrentPhoto = false; 336 boolean stayedOnCamera = false; 337 if (!mPhotoView.getFilmMode()) { 338 stayedOnCamera = true; 339 } else if (SystemClock.uptimeMillis() < mCameraSwitchCutoff && 340 mMediaSet.getMediaItemCount() > 1) { 341 mPhotoView.switchToImage(1); 342 } else { 343 if (mAppBridge != null) mPhotoView.setFilmMode(false); 344 stayedOnCamera = true; 345 } 346 347 if (stayedOnCamera) { 348 if (mAppBridge == null && mMediaSet.getTotalMediaItemCount() > 1) { 349 launchCamera(); 350 /* We got here by swiping from photo 1 to the 351 placeholder, so make it be the thing that 352 is in focus when the user presses back from 353 the camera app */ 354 mPhotoView.switchToImage(1); 355 } else { 356 updateBars(); 357 updateCurrentPhoto(mModel.getMediaItem(0)); 358 } 359 } 360 break; 361 } 362 case MSG_ON_PICTURE_CENTER: { 363 if (!mPhotoView.getFilmMode() && mCurrentPhoto != null 364 && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) { 365 mPhotoView.setFilmMode(true); 366 } 367 break; 368 } 369 case MSG_REFRESH_IMAGE: { 370 final MediaItem photo = mCurrentPhoto; 371 mCurrentPhoto = null; 372 updateCurrentPhoto(photo); 373 break; 374 } 375 case MSG_UPDATE_PHOTO_UI: { 376 updateUIForCurrentPhoto(); 377 break; 378 } 379 case MSG_UPDATE_PROGRESS: { 380 updateProgressBar(); 381 break; 382 } 383 case MSG_UPDATE_SHARE_URI: { 384 if (mCurrentPhoto == message.obj) { 385 boolean isPanorama360 = message.arg1 != 0; 386 Uri contentUri = mCurrentPhoto.getContentUri(); 387 Intent panoramaIntent = null; 388 if (isPanorama360) { 389 panoramaIntent = createSharePanoramaIntent(contentUri); 390 } 391 Intent shareIntent = createShareIntent(mCurrentPhoto); 392 393 mActionBar.setShareIntents(panoramaIntent, shareIntent); 394 setNfcBeamPushUri(contentUri); 395 } 396 break; 397 } 398 case MSG_UPDATE_PANORAMA_UI: { 399 if (mCurrentPhoto == message.obj) { 400 boolean isPanorama360 = message.arg1 != 0; 401 updatePanoramaUI(isPanorama360); 402 } 403 break; 404 } 405 default: throw new AssertionError(message.what); 406 } 407 } 408 }; 409 410 mSetPathString = data.getString(KEY_MEDIA_SET_PATH); 411 mOriginalSetPathString = mSetPathString; 412 setupNfcBeamPush(); 413 String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH); 414 Path itemPath = itemPathString != null ? 415 Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)) : 416 null; 417 mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false); 418 mStartInFilmstrip = data.getBoolean(KEY_START_IN_FILMSTRIP, false); 419 boolean inCameraRoll = data.getBoolean(KEY_IN_CAMERA_ROLL, false); 420 mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0); 421 if (mSetPathString != null) { 422 mShowSpinner = true; 423 mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE); 424 if (mAppBridge != null) { 425 mShowBars = false; 426 mHasCameraScreennailOrPlaceholder = true; 427 mAppBridge.setServer(this); 428 429 // Get the ScreenNail from AppBridge and register it. 430 int id = SnailSource.newId(); 431 Path screenNailSetPath = SnailSource.getSetPath(id); 432 Path screenNailItemPath = SnailSource.getItemPath(id); 433 mScreenNailSet = (SnailAlbum) mActivity.getDataManager() 434 .getMediaObject(screenNailSetPath); 435 mScreenNailItem = (SnailItem) mActivity.getDataManager() 436 .getMediaObject(screenNailItemPath); 437 mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail()); 438 439 if (data.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) { 440 // Set the flag to be on top of the lock screen. 441 mFlags |= FLAG_SHOW_WHEN_LOCKED; 442 } 443 444 // Don't display "empty album" action item for capture intents. 445 if (!mSetPathString.equals("/local/all/0")) { 446 // Check if the path is a secure album. 447 if (SecureSource.isSecurePath(mSetPathString)) { 448 mSecureAlbum = (SecureAlbum) mActivity.getDataManager() 449 .getMediaSet(mSetPathString); 450 mShowSpinner = false; 451 } 452 mSetPathString = "/filter/empty/{"+mSetPathString+"}"; 453 } 454 455 // Combine the original MediaSet with the one for ScreenNail 456 // from AppBridge. 457 mSetPathString = "/combo/item/{" + screenNailSetPath + 458 "," + mSetPathString + "}"; 459 460 // Start from the screen nail. 461 itemPath = screenNailItemPath; 462 } else if (inCameraRoll && GalleryUtils.isCameraAvailable(mActivity)) { 463 mSetPathString = "/combo/item/{" + FilterSource.FILTER_CAMERA_SHORTCUT + 464 "," + mSetPathString + "}"; 465 mCurrentIndex++; 466 mHasCameraScreennailOrPlaceholder = true; 467 } 468 469 MediaSet originalSet = mActivity.getDataManager() 470 .getMediaSet(mSetPathString); 471 if (mHasCameraScreennailOrPlaceholder && originalSet instanceof ComboAlbum) { 472 // Use the name of the camera album rather than the default 473 // ComboAlbum behavior 474 ((ComboAlbum) originalSet).useNameOfChild(1); 475 } 476 mSelectionManager.setSourceMediaSet(originalSet); 477 mSetPathString = "/filter/delete/{" + mSetPathString + "}"; 478 mMediaSet = (FilterDeleteSet) mActivity.getDataManager() 479 .getMediaSet(mSetPathString); 480 if (mMediaSet == null) { 481 Log.w(TAG, "failed to restore " + mSetPathString); 482 } 483 if (itemPath == null) { 484 int mediaItemCount = mMediaSet.getMediaItemCount(); 485 if (mediaItemCount > 0) { 486 if (mCurrentIndex >= mediaItemCount) mCurrentIndex = 0; 487 itemPath = mMediaSet.getMediaItem(mCurrentIndex, 1) 488 .get(0).getPath(); 489 } else { 490 // Bail out, PhotoPage can't load on an empty album 491 return; 492 } 493 } 494 PhotoDataAdapter pda = new PhotoDataAdapter( 495 mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex, 496 mAppBridge == null ? -1 : 0, 497 mAppBridge == null ? false : mAppBridge.isPanorama(), 498 mAppBridge == null ? false : mAppBridge.isStaticCamera()); 499 mModel = pda; 500 mPhotoView.setModel(mModel); 501 502 pda.setDataListener(new PhotoDataAdapter.DataListener() { 503 504 @Override 505 public void onPhotoChanged(int index, Path item) { 506 int oldIndex = mCurrentIndex; 507 mCurrentIndex = index; 508 509 if (mHasCameraScreennailOrPlaceholder) { 510 if (mCurrentIndex > 0) { 511 mSkipUpdateCurrentPhoto = false; 512 } 513 514 if (oldIndex == 0 && mCurrentIndex > 0 515 && !mPhotoView.getFilmMode()) { 516 mPhotoView.setFilmMode(true); 517 if (mAppBridge != null) { 518 UsageStatistics.onEvent("CameraToFilmstrip", 519 UsageStatistics.TRANSITION_SWIPE, null); 520 } 521 } else if (oldIndex == 2 && mCurrentIndex == 1) { 522 mCameraSwitchCutoff = SystemClock.uptimeMillis() + 523 CAMERA_SWITCH_CUTOFF_THRESHOLD_MS; 524 mPhotoView.stopScrolling(); 525 } else if (oldIndex >= 1 && mCurrentIndex == 0) { 526 mPhotoView.setWantPictureCenterCallbacks(true); 527 mSkipUpdateCurrentPhoto = true; 528 } 529 } 530 if (!mSkipUpdateCurrentPhoto) { 531 if (item != null) { 532 MediaItem photo = mModel.getMediaItem(0); 533 if (photo != null) updateCurrentPhoto(photo); 534 } 535 updateBars(); 536 } 537 // Reset the timeout for the bars after a swipe 538 refreshHidingMessage(); 539 } 540 541 @Override 542 public void onLoadingFinished(boolean loadingFailed) { 543 if (!mModel.isEmpty()) { 544 MediaItem photo = mModel.getMediaItem(0); 545 if (photo != null) updateCurrentPhoto(photo); 546 } else if (mIsActive) { 547 // We only want to finish the PhotoPage if there is no 548 // deletion that the user can undo. 549 if (mMediaSet.getNumberOfDeletions() == 0) { 550 mActivity.getStateManager().finishState( 551 PhotoPage.this); 552 } 553 } 554 } 555 556 @Override 557 public void onLoadingStarted() { 558 } 559 }); 560 } else { 561 // Get default media set by the URI 562 MediaItem mediaItem = (MediaItem) 563 mActivity.getDataManager().getMediaObject(itemPath); 564 mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem); 565 mPhotoView.setModel(mModel); 566 updateCurrentPhoto(mediaItem); 567 mShowSpinner = false; 568 } 569 570 mPhotoView.setFilmMode(mStartInFilmstrip && mMediaSet.getMediaItemCount() > 1); 571 RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity) 572 .findViewById(mAppBridge != null ? R.id.content : R.id.gallery_root); 573 if (galleryRoot != null) { 574 if (mSecureAlbum == null) { 575 mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot); 576 } 577 StitchingProgressManager progressManager = mApplication.getStitchingProgressManager(); 578 if (progressManager != null) { 579 mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot); 580 mProgressListener = new UpdateProgressListener(); 581 progressManager.addChangeListener(mProgressListener); 582 if (mSecureAlbum != null) { 583 progressManager.addChangeListener(mSecureAlbum); 584 } 585 } 586 } 587 } 588 589 @Override 590 public void onPictureCenter(boolean isCamera) { 591 isCamera = isCamera || (mHasCameraScreennailOrPlaceholder && mAppBridge == null); 592 mPhotoView.setWantPictureCenterCallbacks(false); 593 mHandler.removeMessages(MSG_ON_CAMERA_CENTER); 594 mHandler.removeMessages(MSG_ON_PICTURE_CENTER); 595 mHandler.sendEmptyMessage(isCamera ? MSG_ON_CAMERA_CENTER : MSG_ON_PICTURE_CENTER); 596 } 597 598 @Override 599 public boolean canDisplayBottomControls() { 600 return mIsActive && !mPhotoView.canUndo(); 601 } 602 603 @Override 604 public boolean canDisplayBottomControl(int control) { 605 if (mCurrentPhoto == null) { 606 return false; 607 } 608 switch(control) { 609 case R.id.photopage_bottom_control_edit: 610 return mHaveImageEditor && mShowBars 611 && !mPhotoView.getFilmMode() 612 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0 613 && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE; 614 case R.id.photopage_bottom_control_panorama: 615 return mIsPanorama; 616 case R.id.photopage_bottom_control_tiny_planet: 617 return mHaveImageEditor && mShowBars 618 && mIsPanorama360 && !mPhotoView.getFilmMode(); 619 default: 620 return false; 621 } 622 } 623 624 @Override 625 public void onBottomControlClicked(int control) { 626 switch(control) { 627 case R.id.photopage_bottom_control_edit: 628 launchPhotoEditor(); 629 return; 630 case R.id.photopage_bottom_control_panorama: 631 mActivity.getPanoramaViewHelper() 632 .showPanorama(mCurrentPhoto.getContentUri()); 633 return; 634 case R.id.photopage_bottom_control_tiny_planet: 635 launchTinyPlanet(); 636 return; 637 default: 638 return; 639 } 640 } 641 642 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 643 private void setupNfcBeamPush() { 644 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return; 645 646 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mActivity); 647 if (adapter != null) { 648 adapter.setBeamPushUris(null, mActivity); 649 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { 650 @Override 651 public Uri[] createBeamUris(NfcEvent event) { 652 return mNfcPushUris; 653 } 654 }, mActivity); 655 } 656 } 657 658 private void setNfcBeamPushUri(Uri uri) { 659 mNfcPushUris[0] = uri; 660 } 661 662 private static Intent createShareIntent(MediaObject mediaObject) { 663 int type = mediaObject.getMediaType(); 664 return new Intent(Intent.ACTION_SEND) 665 .setType(MenuExecutor.getMimeType(type)) 666 .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri()) 667 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 668 } 669 670 private static Intent createSharePanoramaIntent(Uri contentUri) { 671 return new Intent(Intent.ACTION_SEND) 672 .setType(GalleryUtils.MIME_TYPE_PANORAMA360) 673 .putExtra(Intent.EXTRA_STREAM, contentUri) 674 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 675 } 676 677 private void overrideTransitionToEditor() { 678 ((Activity) mActivity).overridePendingTransition(android.R.anim.fade_in, 679 android.R.anim.fade_out); 680 } 681 682 private void launchTinyPlanet() { 683 // Deep link into tiny planet 684 MediaItem current = mModel.getMediaItem(0); 685 Intent intent = new Intent(FilterShowActivity.TINY_PLANET_ACTION); 686 intent.setClass(mActivity, FilterShowActivity.class); 687 intent.setDataAndType(current.getContentUri(), current.getMimeType()) 688 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 689 intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, 690 mActivity.isFullscreen()); 691 mActivity.startActivityForResult(intent, REQUEST_EDIT); 692 overrideTransitionToEditor(); 693 } 694 695 private void launchCamera() { 696 Intent intent = new Intent(mActivity, CameraActivity.class) 697 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 698 mRecenterCameraOnResume = false; 699 mActivity.startActivity(intent); 700 } 701 702 private void launchPhotoEditor() { 703 MediaItem current = mModel.getMediaItem(0); 704 if (current == null || (current.getSupportedOperations() 705 & MediaObject.SUPPORT_EDIT) == 0) { 706 return; 707 } 708 709 Intent intent = new Intent(ACTION_NEXTGEN_EDIT); 710 711 intent.setDataAndType(current.getContentUri(), current.getMimeType()) 712 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 713 if (mActivity.getPackageManager() 714 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) { 715 intent.setAction(Intent.ACTION_EDIT); 716 } 717 intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN, 718 mActivity.isFullscreen()); 719 ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null), 720 REQUEST_EDIT); 721 overrideTransitionToEditor(); 722 } 723 724 private void requestDeferredUpdate() { 725 mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS; 726 if (!mDeferredUpdateWaiting) { 727 mDeferredUpdateWaiting = true; 728 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS); 729 } 730 } 731 732 private void updateUIForCurrentPhoto() { 733 if (mCurrentPhoto == null) return; 734 735 // If by swiping or deletion the user ends up on an action item 736 // and zoomed in, zoom out so that the context of the action is 737 // more clear 738 if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0 739 && !mPhotoView.getFilmMode()) { 740 mPhotoView.setWantPictureCenterCallbacks(true); 741 } 742 743 updateMenuOperations(); 744 refreshBottomControlsWhenReady(); 745 if (mShowDetails) { 746 mDetailsHelper.reloadDetails(); 747 } 748 if ((mSecureAlbum == null) 749 && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { 750 mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback); 751 } 752 updateProgressBar(); 753 } 754 755 private void updateCurrentPhoto(MediaItem photo) { 756 if (mCurrentPhoto == photo) return; 757 mCurrentPhoto = photo; 758 if (mPhotoView.getFilmMode()) { 759 requestDeferredUpdate(); 760 } else { 761 updateUIForCurrentPhoto(); 762 } 763 } 764 765 private void updateProgressBar() { 766 if (mProgressBar != null) { 767 mProgressBar.hideProgress(); 768 StitchingProgressManager progressManager = mApplication.getStitchingProgressManager(); 769 if (progressManager != null && mCurrentPhoto instanceof LocalImage) { 770 Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri()); 771 if (progress != null) { 772 mProgressBar.setProgress(progress); 773 } 774 } 775 } 776 } 777 778 private void updateMenuOperations() { 779 Menu menu = mActionBar.getMenu(); 780 781 // it could be null if onCreateActionBar has not been called yet 782 if (menu == null) return; 783 784 MenuItem item = menu.findItem(R.id.action_slideshow); 785 if (item != null) { 786 item.setVisible((mSecureAlbum == null) && canDoSlideShow()); 787 } 788 if (mCurrentPhoto == null) return; 789 790 int supportedOperations = mCurrentPhoto.getSupportedOperations(); 791 if (mSecureAlbum != null) { 792 supportedOperations &= MediaObject.SUPPORT_DELETE; 793 } else if (!mHaveImageEditor) { 794 supportedOperations &= ~MediaObject.SUPPORT_EDIT; 795 } 796 MenuExecutor.updateMenuOperation(menu, supportedOperations); 797 mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback); 798 } 799 800 private boolean canDoSlideShow() { 801 if (mMediaSet == null || mCurrentPhoto == null) { 802 return false; 803 } 804 if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) { 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 Configuration config = mActivity.getResources().getConfiguration(); 849 if (config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH) { 850 return false; 851 } 852 853 return true; 854 } 855 856 private void wantBars() { 857 if (canShowBars()) showBars(); 858 } 859 860 private void toggleBars() { 861 if (mShowBars) { 862 hideBars(); 863 } else { 864 if (canShowBars()) showBars(); 865 } 866 } 867 868 private void updateBars() { 869 if (!canShowBars()) { 870 hideBars(); 871 } 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 // This is a shield for monkey when it clicks the action bar 1029 // menu when transitioning from filmstrip to camera 1030 if (current instanceof SnailItem) return true; 1031 // TODO: We should check the current photo against the MediaItem 1032 // that the menu was initially created for. We need to fix this 1033 // after PhotoPage being refactored. 1034 if (current == null) { 1035 // item is not ready, ignore 1036 return true; 1037 } 1038 int currentIndex = mModel.getCurrentIndex(); 1039 Path path = current.getPath(); 1040 1041 DataManager manager = mActivity.getDataManager(); 1042 int action = item.getItemId(); 1043 String confirmMsg = null; 1044 switch (action) { 1045 case android.R.id.home: { 1046 onUpPressed(); 1047 return true; 1048 } 1049 case R.id.action_slideshow: { 1050 Bundle data = new Bundle(); 1051 data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString()); 1052 data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString()); 1053 data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex); 1054 data.putBoolean(SlideshowPage.KEY_REPEAT, true); 1055 mActivity.getStateManager().startStateForResult( 1056 SlideshowPage.class, REQUEST_SLIDESHOW, data); 1057 return true; 1058 } 1059 case R.id.action_crop: { 1060 Activity activity = mActivity; 1061 Intent intent = new Intent(FilterShowActivity.CROP_ACTION); 1062 intent.setClass(activity, FilterShowActivity.class); 1063 intent.setDataAndType(manager.getContentUri(path), current.getMimeType()) 1064 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1065 activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current) 1066 ? REQUEST_CROP_PICASA 1067 : REQUEST_CROP); 1068 return true; 1069 } 1070 case R.id.action_trim: { 1071 Intent intent = new Intent(mActivity, TrimVideo.class); 1072 intent.setData(manager.getContentUri(path)); 1073 // We need the file path to wrap this into a RandomAccessFile. 1074 intent.putExtra(KEY_MEDIA_ITEM_PATH, current.getFilePath()); 1075 mActivity.startActivityForResult(intent, REQUEST_TRIM); 1076 return true; 1077 } 1078 case R.id.action_mute: { 1079 MuteVideo muteVideo = new MuteVideo(current.getFilePath(), 1080 manager.getContentUri(path), mActivity); 1081 muteVideo.muteInBackground(); 1082 return true; 1083 } 1084 case R.id.action_edit: { 1085 launchPhotoEditor(); 1086 return true; 1087 } 1088 case R.id.action_details: { 1089 if (mShowDetails) { 1090 hideDetails(); 1091 } else { 1092 showDetails(); 1093 } 1094 return true; 1095 } 1096 case R.id.action_delete: 1097 confirmMsg = mActivity.getResources().getQuantityString( 1098 R.plurals.delete_selection, 1); 1099 case R.id.action_setas: 1100 case R.id.action_rotate_ccw: 1101 case R.id.action_rotate_cw: 1102 case R.id.action_show_on_map: 1103 mSelectionManager.deSelectAll(); 1104 mSelectionManager.toggle(path); 1105 mMenuExecutor.onMenuClicked(item, confirmMsg, mConfirmDialogListener); 1106 return true; 1107 default : 1108 return false; 1109 } 1110 } 1111 1112 private void hideDetails() { 1113 mShowDetails = false; 1114 mDetailsHelper.hide(); 1115 } 1116 1117 private void showDetails() { 1118 mShowDetails = true; 1119 if (mDetailsHelper == null) { 1120 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource()); 1121 mDetailsHelper.setCloseListener(new CloseListener() { 1122 @Override 1123 public void onClose() { 1124 hideDetails(); 1125 } 1126 }); 1127 } 1128 mDetailsHelper.show(); 1129 } 1130 1131 //////////////////////////////////////////////////////////////////////////// 1132 // Callbacks from PhotoView 1133 //////////////////////////////////////////////////////////////////////////// 1134 @Override 1135 public void onSingleTapUp(int x, int y) { 1136 if (mAppBridge != null) { 1137 if (mAppBridge.onSingleTapUp(x, y)) return; 1138 } 1139 1140 MediaItem item = mModel.getMediaItem(0); 1141 if (item == null || item == mScreenNailItem) { 1142 // item is not ready or it is camera preview, ignore 1143 return; 1144 } 1145 1146 int supported = item.getSupportedOperations(); 1147 boolean playVideo = ((supported & MediaItem.SUPPORT_PLAY) != 0); 1148 boolean unlock = ((supported & MediaItem.SUPPORT_UNLOCK) != 0); 1149 boolean goBack = ((supported & MediaItem.SUPPORT_BACK) != 0); 1150 boolean launchCamera = ((supported & MediaItem.SUPPORT_CAMERA_SHORTCUT) != 0); 1151 1152 if (playVideo) { 1153 // determine if the point is at center (1/6) of the photo view. 1154 // (The position of the "play" icon is at center (1/6) of the photo) 1155 int w = mPhotoView.getWidth(); 1156 int h = mPhotoView.getHeight(); 1157 playVideo = (Math.abs(x - w / 2) * 12 <= w) 1158 && (Math.abs(y - h / 2) * 12 <= h); 1159 } 1160 1161 if (playVideo) { 1162 if (mSecureAlbum == null) { 1163 playVideo(mActivity, item.getPlayUri(), item.getName()); 1164 } else { 1165 mActivity.getStateManager().finishState(this); 1166 } 1167 } else if (goBack) { 1168 onBackPressed(); 1169 } else if (unlock) { 1170 Intent intent = new Intent(mActivity, Gallery.class); 1171 intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true); 1172 mActivity.startActivity(intent); 1173 } else if (launchCamera) { 1174 launchCamera(); 1175 } else { 1176 toggleBars(); 1177 } 1178 } 1179 1180 @Override 1181 public void onActionBarAllowed(boolean allowed) { 1182 mActionBarAllowed = allowed; 1183 mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR); 1184 } 1185 1186 @Override 1187 public void onActionBarWanted() { 1188 mHandler.sendEmptyMessage(MSG_WANT_BARS); 1189 } 1190 1191 @Override 1192 public void onFullScreenChanged(boolean full) { 1193 Message m = mHandler.obtainMessage( 1194 MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0); 1195 m.sendToTarget(); 1196 } 1197 1198 // How we do delete/undo: 1199 // 1200 // When the user choose to delete a media item, we just tell the 1201 // FilterDeleteSet to hide that item. If the user choose to undo it, we 1202 // again tell FilterDeleteSet not to hide it. If the user choose to commit 1203 // the deletion, we then actually delete the media item. 1204 @Override 1205 public void onDeleteImage(Path path, int offset) { 1206 onCommitDeleteImage(); // commit the previous deletion 1207 mDeletePath = path; 1208 mDeleteIsFocus = (offset == 0); 1209 mMediaSet.addDeletion(path, mCurrentIndex + offset); 1210 } 1211 1212 @Override 1213 public void onUndoDeleteImage() { 1214 if (mDeletePath == null) return; 1215 // If the deletion was done on the focused item, we want the model to 1216 // focus on it when it is undeleted. 1217 if (mDeleteIsFocus) mModel.setFocusHintPath(mDeletePath); 1218 mMediaSet.removeDeletion(mDeletePath); 1219 mDeletePath = null; 1220 } 1221 1222 @Override 1223 public void onCommitDeleteImage() { 1224 if (mDeletePath == null) return; 1225 mMenuExecutor.startSingleItemAction(R.id.action_delete, mDeletePath); 1226 mDeletePath = null; 1227 } 1228 1229 public void playVideo(Activity activity, Uri uri, String title) { 1230 try { 1231 Intent intent = new Intent(Intent.ACTION_VIEW) 1232 .setDataAndType(uri, "video/*") 1233 .putExtra(Intent.EXTRA_TITLE, title) 1234 .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true); 1235 activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO); 1236 } catch (ActivityNotFoundException e) { 1237 Toast.makeText(activity, activity.getString(R.string.video_err), 1238 Toast.LENGTH_SHORT).show(); 1239 } 1240 } 1241 1242 private void setCurrentPhotoByIntent(Intent intent) { 1243 if (intent == null) return; 1244 Path path = mApplication.getDataManager() 1245 .findPathByUri(intent.getData(), intent.getType()); 1246 if (path != null) { 1247 Path albumPath = mApplication.getDataManager().getDefaultSetOf(path); 1248 if (!albumPath.equalsIgnoreCase(mOriginalSetPathString)) { 1249 // If the edited image is stored in a different album, we need 1250 // to start a new activity state to show the new image 1251 Bundle data = new Bundle(getData()); 1252 data.putString(KEY_MEDIA_SET_PATH, albumPath.toString()); 1253 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString()); 1254 mActivity.getStateManager().startState(SinglePhotoPage.class, data); 1255 return; 1256 } 1257 mModel.setCurrentPhoto(path, mCurrentIndex); 1258 } 1259 } 1260 1261 @Override 1262 protected void onStateResult(int requestCode, int resultCode, Intent data) { 1263 if (resultCode == Activity.RESULT_CANCELED) { 1264 // This is a reset, not a canceled 1265 return; 1266 } 1267 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 1268 // Unmap reset vs. canceled 1269 resultCode = Activity.RESULT_CANCELED; 1270 } 1271 mRecenterCameraOnResume = false; 1272 switch (requestCode) { 1273 case REQUEST_EDIT: 1274 setCurrentPhotoByIntent(data); 1275 break; 1276 case REQUEST_CROP: 1277 if (resultCode == Activity.RESULT_OK) { 1278 setCurrentPhotoByIntent(data); 1279 } 1280 break; 1281 case REQUEST_CROP_PICASA: { 1282 if (resultCode == Activity.RESULT_OK) { 1283 Context context = mActivity.getAndroidContext(); 1284 String message = context.getString(R.string.crop_saved, 1285 context.getString(R.string.folder_edited_online_photos)); 1286 Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); 1287 } 1288 break; 1289 } 1290 case REQUEST_SLIDESHOW: { 1291 if (data == null) break; 1292 String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH); 1293 int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0); 1294 if (path != null) { 1295 mModel.setCurrentPhoto(Path.fromString(path), index); 1296 } 1297 } 1298 } 1299 } 1300 1301 @Override 1302 public void onPause() { 1303 super.onPause(); 1304 mIsActive = false; 1305 1306 mActivity.getGLRoot().unfreeze(); 1307 mHandler.removeMessages(MSG_UNFREEZE_GLROOT); 1308 1309 DetailsHelper.pause(); 1310 // Hide the detail dialog on exit 1311 if (mShowDetails) hideDetails(); 1312 if (mModel != null) { 1313 mModel.pause(); 1314 } 1315 mPhotoView.pause(); 1316 mHandler.removeMessages(MSG_HIDE_BARS); 1317 mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS); 1318 refreshBottomControlsWhenReady(); 1319 mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener); 1320 if (mShowSpinner) { 1321 mActionBar.disableAlbumModeMenu(true); 1322 } 1323 onCommitDeleteImage(); 1324 mMenuExecutor.pause(); 1325 if (mMediaSet != null) mMediaSet.clearDeletion(); 1326 } 1327 1328 @Override 1329 public void onCurrentImageUpdated() { 1330 mActivity.getGLRoot().unfreeze(); 1331 } 1332 1333 @Override 1334 public void onFilmModeChanged(boolean enabled) { 1335 refreshBottomControlsWhenReady(); 1336 if (mShowSpinner) { 1337 if (enabled) { 1338 mActionBar.enableAlbumModeMenu( 1339 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); 1340 } else { 1341 mActionBar.disableAlbumModeMenu(true); 1342 } 1343 } 1344 if (enabled) { 1345 mHandler.removeMessages(MSG_HIDE_BARS); 1346 UsageStatistics.onContentViewChanged( 1347 UsageStatistics.COMPONENT_GALLERY, "FilmstripPage"); 1348 } else { 1349 refreshHidingMessage(); 1350 if (mAppBridge == null || mCurrentIndex > 0) { 1351 UsageStatistics.onContentViewChanged( 1352 UsageStatistics.COMPONENT_GALLERY, "SinglePhotoPage"); 1353 } else { 1354 UsageStatistics.onContentViewChanged( 1355 UsageStatistics.COMPONENT_CAMERA, "Unknown"); // TODO 1356 } 1357 } 1358 } 1359 1360 private void transitionFromAlbumPageIfNeeded() { 1361 TransitionStore transitions = mActivity.getTransitionStore(); 1362 1363 int albumPageTransition = transitions.get( 1364 KEY_ALBUMPAGE_TRANSITION, MSG_ALBUMPAGE_NONE); 1365 1366 if (albumPageTransition == MSG_ALBUMPAGE_NONE && mAppBridge != null 1367 && mRecenterCameraOnResume) { 1368 // Generally, resuming the PhotoPage when in Camera should 1369 // reset to the capture mode to allow quick photo taking 1370 mCurrentIndex = 0; 1371 mPhotoView.resetToFirstPicture(); 1372 } else { 1373 int resumeIndex = transitions.get(KEY_INDEX_HINT, -1); 1374 if (resumeIndex >= 0) { 1375 if (mHasCameraScreennailOrPlaceholder) { 1376 // Account for preview/placeholder being the first item 1377 resumeIndex++; 1378 } 1379 if (resumeIndex < mMediaSet.getMediaItemCount()) { 1380 mCurrentIndex = resumeIndex; 1381 mModel.moveTo(mCurrentIndex); 1382 } 1383 } 1384 } 1385 1386 if (albumPageTransition == MSG_ALBUMPAGE_RESUMED) { 1387 mPhotoView.setFilmMode(mStartInFilmstrip || mAppBridge != null); 1388 } else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) { 1389 mPhotoView.setFilmMode(false); 1390 } 1391 } 1392 1393 @Override 1394 protected void onResume() { 1395 super.onResume(); 1396 1397 if (mModel == null) { 1398 mActivity.getStateManager().finishState(this); 1399 return; 1400 } 1401 transitionFromAlbumPageIfNeeded(); 1402 1403 mActivity.getGLRoot().freeze(); 1404 mIsActive = true; 1405 setContentPane(mRootPane); 1406 1407 mModel.resume(); 1408 mPhotoView.resume(); 1409 mActionBar.setDisplayOptions( 1410 ((mSecureAlbum == null) && (mSetPathString != null)), false); 1411 mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener); 1412 refreshBottomControlsWhenReady(); 1413 if (mShowSpinner && mPhotoView.getFilmMode()) { 1414 mActionBar.enableAlbumModeMenu( 1415 GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this); 1416 } 1417 if (!mShowBars) { 1418 mActionBar.hide(); 1419 mActivity.getGLRoot().setLightsOutMode(true); 1420 } 1421 boolean haveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*"); 1422 if (haveImageEditor != mHaveImageEditor) { 1423 mHaveImageEditor = haveImageEditor; 1424 updateMenuOperations(); 1425 } 1426 1427 mRecenterCameraOnResume = true; 1428 mHandler.sendEmptyMessageDelayed(MSG_UNFREEZE_GLROOT, UNFREEZE_GLROOT_TIMEOUT); 1429 } 1430 1431 @Override 1432 protected void onDestroy() { 1433 if (mAppBridge != null) { 1434 mAppBridge.setServer(null); 1435 mScreenNailItem.setScreenNail(null); 1436 mAppBridge.detachScreenNail(); 1437 mAppBridge = null; 1438 mScreenNailSet = null; 1439 mScreenNailItem = null; 1440 } 1441 mActivity.getGLRoot().setOrientationSource(null); 1442 if (mBottomControls != null) mBottomControls.cleanup(); 1443 1444 // Remove all pending messages. 1445 mHandler.removeCallbacksAndMessages(null); 1446 super.onDestroy(); 1447 } 1448 1449 private class MyDetailsSource implements DetailsSource { 1450 1451 @Override 1452 public MediaDetails getDetails() { 1453 return mModel.getMediaItem(0).getDetails(); 1454 } 1455 1456 @Override 1457 public int size() { 1458 return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1; 1459 } 1460 1461 @Override 1462 public int setIndex() { 1463 return mModel.getCurrentIndex(); 1464 } 1465 } 1466 1467 @Override 1468 public void onAlbumModeSelected(int mode) { 1469 if (mode == GalleryActionBar.ALBUM_GRID_MODE_SELECTED) { 1470 switchToGrid(); 1471 } 1472 } 1473 1474 @Override 1475 public void refreshBottomControlsWhenReady() { 1476 if (mBottomControls == null) { 1477 return; 1478 } 1479 MediaObject currentPhoto = mCurrentPhoto; 1480 if (currentPhoto == null) { 1481 mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, 0, 0, currentPhoto).sendToTarget(); 1482 } else { 1483 currentPhoto.getPanoramaSupport(mRefreshBottomControlsCallback); 1484 } 1485 } 1486 1487 private void updatePanoramaUI(boolean isPanorama360) { 1488 Menu menu = mActionBar.getMenu(); 1489 1490 // it could be null if onCreateActionBar has not been called yet 1491 if (menu == null) { 1492 return; 1493 } 1494 1495 MenuExecutor.updateMenuForPanorama(menu, isPanorama360, isPanorama360); 1496 1497 if (isPanorama360) { 1498 MenuItem item = menu.findItem(R.id.action_share); 1499 if (item != null) { 1500 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1501 item.setTitle(mActivity.getResources().getString(R.string.share_as_photo)); 1502 } 1503 } else if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_SHARE) != 0) { 1504 MenuItem item = menu.findItem(R.id.action_share); 1505 if (item != null) { 1506 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1507 item.setTitle(mActivity.getResources().getString(R.string.share)); 1508 } 1509 } 1510 } 1511 1512 @Override 1513 public void onUndoBarVisibilityChanged(boolean visible) { 1514 refreshBottomControlsWhenReady(); 1515 } 1516} 1517