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