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