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