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