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