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