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