summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/app/PhotoPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/app/PhotoPage.java')
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java1571
1 files changed, 1571 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
new file mode 100644
index 000000000..7a71e9109
--- /dev/null
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -0,0 +1,1571 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+import android.annotation.TargetApi;
+import android.app.ActionBar.OnMenuVisibilityListener;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.CreateBeamUrisCallback;
+import android.nfc.NfcEvent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.RelativeLayout;
+import android.widget.ShareActionProvider;
+import android.widget.Toast;
+
+import com.android.camera.CameraActivity;
+import com.android.camera.ProxyLauncher;
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.data.ComboAlbum;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.FilterDeleteSet;
+import com.android.gallery3d.data.FilterSource;
+import com.android.gallery3d.data.LocalImage;
+import com.android.gallery3d.data.MediaDetails;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.data.SecureAlbum;
+import com.android.gallery3d.data.SecureSource;
+import com.android.gallery3d.data.SnailAlbum;
+import com.android.gallery3d.data.SnailItem;
+import com.android.gallery3d.data.SnailSource;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.crop.CropActivity;
+import com.android.gallery3d.picasasource.PicasaSource;
+import com.android.gallery3d.ui.DetailsHelper;
+import com.android.gallery3d.ui.DetailsHelper.CloseListener;
+import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
+import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.MenuExecutor;
+import com.android.gallery3d.ui.PhotoView;
+import com.android.gallery3d.ui.SelectionManager;
+import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.util.GalleryUtils;
+import com.android.gallery3d.util.UsageStatistics;
+
+public abstract class PhotoPage extends ActivityState implements
+ PhotoView.Listener, AppBridge.Server, ShareActionProvider.OnShareTargetSelectedListener,
+ PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener {
+ private static final String TAG = "PhotoPage";
+
+ private static final int MSG_HIDE_BARS = 1;
+ private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
+ private static final int MSG_UPDATE_ACTION_BAR = 5;
+ private static final int MSG_UNFREEZE_GLROOT = 6;
+ private static final int MSG_WANT_BARS = 7;
+ private static final int MSG_REFRESH_BOTTOM_CONTROLS = 8;
+ private static final int MSG_ON_CAMERA_CENTER = 9;
+ private static final int MSG_ON_PICTURE_CENTER = 10;
+ private static final int MSG_REFRESH_IMAGE = 11;
+ private static final int MSG_UPDATE_PHOTO_UI = 12;
+ private static final int MSG_UPDATE_PROGRESS = 13;
+ private static final int MSG_UPDATE_DEFERRED = 14;
+ private static final int MSG_UPDATE_SHARE_URI = 15;
+ private static final int MSG_UPDATE_PANORAMA_UI = 16;
+
+ private static final int HIDE_BARS_TIMEOUT = 3500;
+ private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
+
+ private static final int REQUEST_SLIDESHOW = 1;
+ private static final int REQUEST_CROP = 2;
+ private static final int REQUEST_CROP_PICASA = 3;
+ private static final int REQUEST_EDIT = 4;
+ private static final int REQUEST_PLAY_VIDEO = 5;
+ private static final int REQUEST_TRIM = 6;
+
+ public static final String KEY_MEDIA_SET_PATH = "media-set-path";
+ public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
+ public static final String KEY_INDEX_HINT = "index-hint";
+ public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
+ public static final String KEY_APP_BRIDGE = "app-bridge";
+ public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up";
+ public static final String KEY_START_IN_FILMSTRIP = "start-in-filmstrip";
+ public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
+ public static final String KEY_SHOW_WHEN_LOCKED = "show_when_locked";
+ public static final String KEY_IN_CAMERA_ROLL = "in_camera_roll";
+
+ public static final String KEY_ALBUMPAGE_TRANSITION = "albumpage-transition";
+ public static final int MSG_ALBUMPAGE_NONE = 0;
+ public static final int MSG_ALBUMPAGE_STARTED = 1;
+ public static final int MSG_ALBUMPAGE_RESUMED = 2;
+ public static final int MSG_ALBUMPAGE_PICKED = 4;
+
+ public static final String ACTION_NEXTGEN_EDIT = "action_nextgen_edit";
+ public static final String ACTION_SIMPLE_EDIT = "action_simple_edit";
+
+ private GalleryApp mApplication;
+ private SelectionManager mSelectionManager;
+
+ private PhotoView mPhotoView;
+ private PhotoPage.Model mModel;
+ private DetailsHelper mDetailsHelper;
+ private boolean mShowDetails;
+
+ // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied.
+ // E.g., viewing a photo in gmail attachment
+ private FilterDeleteSet mMediaSet;
+
+ // The mediaset used by camera launched from secure lock screen.
+ private SecureAlbum mSecureAlbum;
+
+ private int mCurrentIndex = 0;
+ private Handler mHandler;
+ private boolean mShowBars = true;
+ private volatile boolean mActionBarAllowed = true;
+ private GalleryActionBar mActionBar;
+ private boolean mIsMenuVisible;
+ private boolean mHaveImageEditor;
+ private PhotoPageBottomControls mBottomControls;
+ private PhotoPageProgressBar mProgressBar;
+ private MediaItem mCurrentPhoto = null;
+ private MenuExecutor mMenuExecutor;
+ private boolean mIsActive;
+ private boolean mShowSpinner;
+ private String mSetPathString;
+ // This is the original mSetPathString before adding the camera preview item.
+ private String mOriginalSetPathString;
+ private AppBridge mAppBridge;
+ private SnailItem mScreenNailItem;
+ private SnailAlbum mScreenNailSet;
+ private OrientationManager mOrientationManager;
+ private boolean mTreatBackAsUp;
+ private boolean mStartInFilmstrip;
+ private boolean mHasCameraScreennailOrPlaceholder = false;
+ private boolean mRecenterCameraOnResume = true;
+
+ // These are only valid after the panorama callback
+ private boolean mIsPanorama;
+ private boolean mIsPanorama360;
+
+ private long mCameraSwitchCutoff = 0;
+ private boolean mSkipUpdateCurrentPhoto = false;
+ private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300;
+
+ private static final long DEFERRED_UPDATE_MS = 250;
+ private boolean mDeferredUpdateWaiting = false;
+ private long mDeferUpdateUntil = Long.MAX_VALUE;
+
+ // The item that is deleted (but it can still be undeleted before commiting)
+ private Path mDeletePath;
+ private boolean mDeleteIsFocus; // whether the deleted item was in focus
+
+ private Uri[] mNfcPushUris = new Uri[1];
+
+ private final MyMenuVisibilityListener mMenuVisibilityListener =
+ new MyMenuVisibilityListener();
+ private UpdateProgressListener mProgressListener;
+
+ private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_UPDATE_PANORAMA_UI, isPanorama360 ? 1 : 0, 0,
+ mediaObject).sendToTarget();
+ }
+ }
+ };
+
+ private final PanoramaSupportCallback mRefreshBottomControlsCallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, isPanorama ? 1 : 0, isPanorama360 ? 1 : 0,
+ mediaObject).sendToTarget();
+ }
+ }
+ };
+
+ private final PanoramaSupportCallback mUpdateShareURICallback = new PanoramaSupportCallback() {
+ @Override
+ public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
+ boolean isPanorama360) {
+ if (mediaObject == mCurrentPhoto) {
+ mHandler.obtainMessage(MSG_UPDATE_SHARE_URI, isPanorama360 ? 1 : 0, 0, mediaObject)
+ .sendToTarget();
+ }
+ }
+ };
+
+ public static interface Model extends PhotoView.Model {
+ public void resume();
+ public void pause();
+ public boolean isEmpty();
+ public void setCurrentPhoto(Path path, int indexHint);
+ }
+
+ private class MyMenuVisibilityListener implements OnMenuVisibilityListener {
+ @Override
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ mIsMenuVisible = isVisible;
+ refreshHidingMessage();
+ }
+ }
+
+ private class UpdateProgressListener implements StitchingChangeListener {
+
+ @Override
+ public void onStitchingResult(Uri uri) {
+ sendUpdate(uri, MSG_REFRESH_IMAGE);
+ }
+
+ @Override
+ public void onStitchingQueued(Uri uri) {
+ sendUpdate(uri, MSG_UPDATE_PROGRESS);
+ }
+
+ @Override
+ public void onStitchingProgress(Uri uri, final int progress) {
+ sendUpdate(uri, MSG_UPDATE_PROGRESS);
+ }
+
+ private void sendUpdate(Uri uri, int message) {
+ MediaObject currentPhoto = mCurrentPhoto;
+ boolean isCurrentPhoto = currentPhoto instanceof LocalImage
+ && currentPhoto.getContentUri().equals(uri);
+ if (isCurrentPhoto) {
+ mHandler.sendEmptyMessage(message);
+ }
+ }
+ };
+
+ @Override
+ protected int getBackgroundColorId() {
+ return R.color.photo_background;
+ }
+
+ private final GLView mRootPane = new GLView() {
+ @Override
+ protected void onLayout(
+ boolean changed, int left, int top, int right, int bottom) {
+ mPhotoView.layout(0, 0, right - left, bottom - top);
+ if (mShowDetails) {
+ mDetailsHelper.layout(left, mActionBar.getHeight(), right, bottom);
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle data, Bundle restoreState) {
+ super.onCreate(data, restoreState);
+ mActionBar = mActivity.getGalleryActionBar();
+ mSelectionManager = new SelectionManager(mActivity, false);
+ mMenuExecutor = new MenuExecutor(mActivity, mSelectionManager);
+
+ mPhotoView = new PhotoView(mActivity);
+ mPhotoView.setListener(this);
+ mRootPane.addComponent(mPhotoView);
+ mApplication = (GalleryApp) ((Activity) mActivity).getApplication();
+ mOrientationManager = mActivity.getOrientationManager();
+ mActivity.getGLRoot().setOrientationSource(mOrientationManager);
+
+ mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_HIDE_BARS: {
+ hideBars();
+ break;
+ }
+ case MSG_REFRESH_BOTTOM_CONTROLS: {
+ if (mCurrentPhoto == message.obj && mBottomControls != null) {
+ mIsPanorama = message.arg1 == 1;
+ mIsPanorama360 = message.arg2 == 1;
+ mBottomControls.refresh();
+ }
+ break;
+ }
+ case MSG_ON_FULL_SCREEN_CHANGED: {
+ if (mAppBridge != null) {
+ mAppBridge.onFullScreenChanged(message.arg1 == 1);
+ }
+ break;
+ }
+ case MSG_UPDATE_ACTION_BAR: {
+ updateBars();
+ break;
+ }
+ case MSG_WANT_BARS: {
+ wantBars();
+ break;
+ }
+ case MSG_UNFREEZE_GLROOT: {
+ mActivity.getGLRoot().unfreeze();
+ break;
+ }
+ case MSG_UPDATE_DEFERRED: {
+ long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis();
+ if (nextUpdate <= 0) {
+ mDeferredUpdateWaiting = false;
+ updateUIForCurrentPhoto();
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate);
+ }
+ break;
+ }
+ case MSG_ON_CAMERA_CENTER: {
+ mSkipUpdateCurrentPhoto = false;
+ boolean stayedOnCamera = false;
+ if (!mPhotoView.getFilmMode()) {
+ stayedOnCamera = true;
+ } else if (SystemClock.uptimeMillis() < mCameraSwitchCutoff &&
+ mMediaSet.getMediaItemCount() > 1) {
+ mPhotoView.switchToImage(1);
+ } else {
+ if (mAppBridge != null) mPhotoView.setFilmMode(false);
+ stayedOnCamera = true;
+ }
+
+ if (stayedOnCamera) {
+ if (mAppBridge == null && mMediaSet.getTotalMediaItemCount() > 1) {
+ launchCamera();
+ /* We got here by swiping from photo 1 to the
+ placeholder, so make it be the thing that
+ is in focus when the user presses back from
+ the camera app */
+ mPhotoView.switchToImage(1);
+ } else {
+ updateBars();
+ updateCurrentPhoto(mModel.getMediaItem(0));
+ }
+ }
+ break;
+ }
+ case MSG_ON_PICTURE_CENTER: {
+ if (!mPhotoView.getFilmMode() && mCurrentPhoto != null
+ && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) {
+ mPhotoView.setFilmMode(true);
+ }
+ break;
+ }
+ case MSG_REFRESH_IMAGE: {
+ final MediaItem photo = mCurrentPhoto;
+ mCurrentPhoto = null;
+ updateCurrentPhoto(photo);
+ break;
+ }
+ case MSG_UPDATE_PHOTO_UI: {
+ updateUIForCurrentPhoto();
+ break;
+ }
+ case MSG_UPDATE_PROGRESS: {
+ updateProgressBar();
+ break;
+ }
+ case MSG_UPDATE_SHARE_URI: {
+ if (mCurrentPhoto == message.obj) {
+ boolean isPanorama360 = message.arg1 != 0;
+ Uri contentUri = mCurrentPhoto.getContentUri();
+ Intent panoramaIntent = null;
+ if (isPanorama360) {
+ panoramaIntent = createSharePanoramaIntent(contentUri);
+ }
+ Intent shareIntent = createShareIntent(mCurrentPhoto);
+
+ mActionBar.setShareIntents(panoramaIntent, shareIntent, PhotoPage.this);
+ setNfcBeamPushUri(contentUri);
+ }
+ break;
+ }
+ case MSG_UPDATE_PANORAMA_UI: {
+ if (mCurrentPhoto == message.obj) {
+ boolean isPanorama360 = message.arg1 != 0;
+ updatePanoramaUI(isPanorama360);
+ }
+ break;
+ }
+ default: throw new AssertionError(message.what);
+ }
+ }
+ };
+
+ mSetPathString = data.getString(KEY_MEDIA_SET_PATH);
+ mOriginalSetPathString = mSetPathString;
+ setupNfcBeamPush();
+ String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH);
+ Path itemPath = itemPathString != null ?
+ Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)) :
+ null;
+ mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false);
+ mStartInFilmstrip = data.getBoolean(KEY_START_IN_FILMSTRIP, false);
+ boolean inCameraRoll = data.getBoolean(KEY_IN_CAMERA_ROLL, false);
+ mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
+ if (mSetPathString != null) {
+ mShowSpinner = true;
+ mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
+ if (mAppBridge != null) {
+ mShowBars = false;
+ mHasCameraScreennailOrPlaceholder = true;
+ mAppBridge.setServer(this);
+
+ // Get the ScreenNail from AppBridge and register it.
+ int id = SnailSource.newId();
+ Path screenNailSetPath = SnailSource.getSetPath(id);
+ Path screenNailItemPath = SnailSource.getItemPath(id);
+ mScreenNailSet = (SnailAlbum) mActivity.getDataManager()
+ .getMediaObject(screenNailSetPath);
+ mScreenNailItem = (SnailItem) mActivity.getDataManager()
+ .getMediaObject(screenNailItemPath);
+ mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail());
+
+ if (data.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) {
+ // Set the flag to be on top of the lock screen.
+ mFlags |= FLAG_SHOW_WHEN_LOCKED;
+ }
+
+ // Don't display "empty album" action item for capture intents.
+ if (!mSetPathString.equals("/local/all/0")) {
+ // Check if the path is a secure album.
+ if (SecureSource.isSecurePath(mSetPathString)) {
+ mSecureAlbum = (SecureAlbum) mActivity.getDataManager()
+ .getMediaSet(mSetPathString);
+ mShowSpinner = false;
+ }
+ mSetPathString = "/filter/empty/{"+mSetPathString+"}";
+ }
+
+ // Combine the original MediaSet with the one for ScreenNail
+ // from AppBridge.
+ mSetPathString = "/combo/item/{" + screenNailSetPath +
+ "," + mSetPathString + "}";
+
+ // Start from the screen nail.
+ itemPath = screenNailItemPath;
+ } else if (inCameraRoll && GalleryUtils.isCameraAvailable(mActivity)) {
+ mSetPathString = "/combo/item/{" + FilterSource.FILTER_CAMERA_SHORTCUT +
+ "," + mSetPathString + "}";
+ mCurrentIndex++;
+ mHasCameraScreennailOrPlaceholder = true;
+ }
+
+ MediaSet originalSet = mActivity.getDataManager()
+ .getMediaSet(mSetPathString);
+ if (mHasCameraScreennailOrPlaceholder && originalSet instanceof ComboAlbum) {
+ // Use the name of the camera album rather than the default
+ // ComboAlbum behavior
+ ((ComboAlbum) originalSet).useNameOfChild(1);
+ }
+ mSelectionManager.setSourceMediaSet(originalSet);
+ mSetPathString = "/filter/delete/{" + mSetPathString + "}";
+ mMediaSet = (FilterDeleteSet) mActivity.getDataManager()
+ .getMediaSet(mSetPathString);
+ if (mMediaSet == null) {
+ Log.w(TAG, "failed to restore " + mSetPathString);
+ }
+ if (itemPath == null) {
+ int mediaItemCount = mMediaSet.getMediaItemCount();
+ if (mediaItemCount > 0) {
+ if (mCurrentIndex >= mediaItemCount) mCurrentIndex = 0;
+ itemPath = mMediaSet.getMediaItem(mCurrentIndex, 1)
+ .get(0).getPath();
+ } else {
+ // Bail out, PhotoPage can't load on an empty album
+ return;
+ }
+ }
+ PhotoDataAdapter pda = new PhotoDataAdapter(
+ mActivity, mPhotoView, mMediaSet, itemPath, mCurrentIndex,
+ mAppBridge == null ? -1 : 0,
+ mAppBridge == null ? false : mAppBridge.isPanorama(),
+ mAppBridge == null ? false : mAppBridge.isStaticCamera());
+ mModel = pda;
+ mPhotoView.setModel(mModel);
+
+ pda.setDataListener(new PhotoDataAdapter.DataListener() {
+
+ @Override
+ public void onPhotoChanged(int index, Path item) {
+ int oldIndex = mCurrentIndex;
+ mCurrentIndex = index;
+
+ if (mHasCameraScreennailOrPlaceholder) {
+ if (mCurrentIndex > 0) {
+ mSkipUpdateCurrentPhoto = false;
+ }
+
+ if (oldIndex == 0 && mCurrentIndex > 0
+ && !mPhotoView.getFilmMode()) {
+ mPhotoView.setFilmMode(true);
+ if (mAppBridge != null) {
+ UsageStatistics.onEvent("CameraToFilmstrip",
+ UsageStatistics.TRANSITION_SWIPE, null);
+ }
+ } else if (oldIndex == 2 && mCurrentIndex == 1) {
+ mCameraSwitchCutoff = SystemClock.uptimeMillis() +
+ CAMERA_SWITCH_CUTOFF_THRESHOLD_MS;
+ mPhotoView.stopScrolling();
+ } else if (oldIndex >= 1 && mCurrentIndex == 0) {
+ mPhotoView.setWantPictureCenterCallbacks(true);
+ mSkipUpdateCurrentPhoto = true;
+ }
+ }
+ if (!mSkipUpdateCurrentPhoto) {
+ if (item != null) {
+ MediaItem photo = mModel.getMediaItem(0);
+ if (photo != null) updateCurrentPhoto(photo);
+ }
+ updateBars();
+ }
+ // Reset the timeout for the bars after a swipe
+ refreshHidingMessage();
+ }
+
+ @Override
+ public void onLoadingFinished(boolean loadingFailed) {
+ if (!mModel.isEmpty()) {
+ MediaItem photo = mModel.getMediaItem(0);
+ if (photo != null) updateCurrentPhoto(photo);
+ } else if (mIsActive) {
+ // We only want to finish the PhotoPage if there is no
+ // deletion that the user can undo.
+ if (mMediaSet.getNumberOfDeletions() == 0) {
+ mActivity.getStateManager().finishState(
+ PhotoPage.this);
+ }
+ }
+ }
+
+ @Override
+ public void onLoadingStarted() {
+ }
+ });
+ } else {
+ // Get default media set by the URI
+ MediaItem mediaItem = (MediaItem)
+ mActivity.getDataManager().getMediaObject(itemPath);
+ mModel = new SinglePhotoDataAdapter(mActivity, mPhotoView, mediaItem);
+ mPhotoView.setModel(mModel);
+ updateCurrentPhoto(mediaItem);
+ mShowSpinner = false;
+ }
+
+ mPhotoView.setFilmMode(mStartInFilmstrip && mMediaSet.getMediaItemCount() > 1);
+ RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity)
+ .findViewById(mAppBridge != null ? R.id.content : R.id.gallery_root);
+ if (galleryRoot != null) {
+ if (mSecureAlbum == null) {
+ mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot);
+ }
+ StitchingProgressManager progressManager = mApplication.getStitchingProgressManager();
+ if (progressManager != null) {
+ mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot);
+ mProgressListener = new UpdateProgressListener();
+ progressManager.addChangeListener(mProgressListener);
+ if (mSecureAlbum != null) {
+ progressManager.addChangeListener(mSecureAlbum);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onPictureCenter(boolean isCamera) {
+ isCamera = isCamera || (mHasCameraScreennailOrPlaceholder && mAppBridge == null);
+ mPhotoView.setWantPictureCenterCallbacks(false);
+ mHandler.removeMessages(MSG_ON_CAMERA_CENTER);
+ mHandler.removeMessages(MSG_ON_PICTURE_CENTER);
+ mHandler.sendEmptyMessage(isCamera ? MSG_ON_CAMERA_CENTER : MSG_ON_PICTURE_CENTER);
+ }
+
+ @Override
+ public boolean canDisplayBottomControls() {
+ return mIsActive && !mPhotoView.canUndo();
+ }
+
+ @Override
+ public boolean canDisplayBottomControl(int control) {
+ if (mCurrentPhoto == null) {
+ return false;
+ }
+ switch(control) {
+ case R.id.photopage_bottom_control_edit:
+ return mHaveImageEditor && mShowBars
+ && !mPhotoView.getFilmMode()
+ && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0
+ && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE;
+ case R.id.photopage_bottom_control_panorama:
+ return mIsPanorama;
+ case R.id.photopage_bottom_control_tiny_planet:
+ return mHaveImageEditor && mShowBars
+ && mIsPanorama360 && !mPhotoView.getFilmMode();
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onBottomControlClicked(int control) {
+ switch(control) {
+ case R.id.photopage_bottom_control_edit:
+ launchPhotoEditor();
+ return;
+ case R.id.photopage_bottom_control_panorama:
+ mActivity.getPanoramaViewHelper()
+ .showPanorama(mCurrentPhoto.getContentUri());
+ return;
+ case R.id.photopage_bottom_control_tiny_planet:
+ launchTinyPlanet();
+ return;
+ default:
+ return;
+ }
+ }
+
+ @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
+ private void setupNfcBeamPush() {
+ if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return;
+
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mActivity);
+ if (adapter != null) {
+ adapter.setBeamPushUris(null, mActivity);
+ adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
+ @Override
+ public Uri[] createBeamUris(NfcEvent event) {
+ return mNfcPushUris;
+ }
+ }, mActivity);
+ }
+ }
+
+ private void setNfcBeamPushUri(Uri uri) {
+ mNfcPushUris[0] = uri;
+ }
+
+ private static Intent createShareIntent(MediaObject mediaObject) {
+ int type = mediaObject.getMediaType();
+ return new Intent(Intent.ACTION_SEND)
+ .setType(MenuExecutor.getMimeType(type))
+ .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri())
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+
+ private static Intent createSharePanoramaIntent(Uri contentUri) {
+ return new Intent(Intent.ACTION_SEND)
+ .setType(GalleryUtils.MIME_TYPE_PANORAMA360)
+ .putExtra(Intent.EXTRA_STREAM, contentUri)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+
+ private void overrideTransitionToEditor() {
+ ((Activity) mActivity).overridePendingTransition(android.R.anim.fade_in,
+ android.R.anim.fade_out);
+ }
+
+ private void launchTinyPlanet() {
+ // Deep link into tiny planet
+ MediaItem current = mModel.getMediaItem(0);
+ Intent intent = new Intent(FilterShowActivity.TINY_PLANET_ACTION);
+ intent.setClass(mActivity, FilterShowActivity.class);
+ intent.setDataAndType(current.getContentUri(), current.getMimeType())
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN,
+ mActivity.isFullscreen());
+ mActivity.startActivityForResult(intent, REQUEST_EDIT);
+ overrideTransitionToEditor();
+ }
+
+ private void launchCamera() {
+ Intent intent = new Intent(mActivity, CameraActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mRecenterCameraOnResume = false;
+ mActivity.startActivity(intent);
+ }
+
+ private void launchPhotoEditor() {
+ MediaItem current = mModel.getMediaItem(0);
+ if (current == null || (current.getSupportedOperations()
+ & MediaObject.SUPPORT_EDIT) == 0) {
+ return;
+ }
+
+ Intent intent = new Intent(ACTION_NEXTGEN_EDIT);
+
+ intent.setDataAndType(current.getContentUri(), current.getMimeType())
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (mActivity.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) {
+ intent.setAction(Intent.ACTION_EDIT);
+ }
+ intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN,
+ mActivity.isFullscreen());
+ ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null),
+ REQUEST_EDIT);
+ overrideTransitionToEditor();
+ }
+
+ private void launchSimpleEditor() {
+ MediaItem current = mModel.getMediaItem(0);
+ if (current == null || (current.getSupportedOperations()
+ & MediaObject.SUPPORT_EDIT) == 0) {
+ return;
+ }
+
+ Intent intent = new Intent(ACTION_SIMPLE_EDIT);
+
+ intent.setDataAndType(current.getContentUri(), current.getMimeType())
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (mActivity.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() == 0) {
+ intent.setAction(Intent.ACTION_EDIT);
+ }
+ intent.putExtra(FilterShowActivity.LAUNCH_FULLSCREEN,
+ mActivity.isFullscreen());
+ ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null),
+ REQUEST_EDIT);
+ overrideTransitionToEditor();
+ }
+
+ private void requestDeferredUpdate() {
+ mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS;
+ if (!mDeferredUpdateWaiting) {
+ mDeferredUpdateWaiting = true;
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS);
+ }
+ }
+
+ private void updateUIForCurrentPhoto() {
+ if (mCurrentPhoto == null) return;
+
+ // If by swiping or deletion the user ends up on an action item
+ // and zoomed in, zoom out so that the context of the action is
+ // more clear
+ if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0
+ && !mPhotoView.getFilmMode()) {
+ mPhotoView.setWantPictureCenterCallbacks(true);
+ }
+
+ updateMenuOperations();
+ refreshBottomControlsWhenReady();
+ if (mShowDetails) {
+ mDetailsHelper.reloadDetails();
+ }
+ if ((mSecureAlbum == null)
+ && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) {
+ mCurrentPhoto.getPanoramaSupport(mUpdateShareURICallback);
+ }
+ updateProgressBar();
+ }
+
+ private void updateCurrentPhoto(MediaItem photo) {
+ if (mCurrentPhoto == photo) return;
+ mCurrentPhoto = photo;
+ if (mPhotoView.getFilmMode()) {
+ requestDeferredUpdate();
+ } else {
+ updateUIForCurrentPhoto();
+ }
+ }
+
+ private void updateProgressBar() {
+ if (mProgressBar != null) {
+ mProgressBar.hideProgress();
+ StitchingProgressManager progressManager = mApplication.getStitchingProgressManager();
+ if (progressManager != null && mCurrentPhoto instanceof LocalImage) {
+ Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri());
+ if (progress != null) {
+ mProgressBar.setProgress(progress);
+ }
+ }
+ }
+ }
+
+ private void updateMenuOperations() {
+ Menu menu = mActionBar.getMenu();
+
+ // it could be null if onCreateActionBar has not been called yet
+ if (menu == null) return;
+
+ MenuItem item = menu.findItem(R.id.action_slideshow);
+ if (item != null) {
+ item.setVisible((mSecureAlbum == null) && canDoSlideShow());
+ }
+ if (mCurrentPhoto == null) return;
+
+ int supportedOperations = mCurrentPhoto.getSupportedOperations();
+ if (mSecureAlbum != null) {
+ supportedOperations &= MediaObject.SUPPORT_DELETE;
+ } else {
+ mCurrentPhoto.getPanoramaSupport(mUpdatePanoramaMenuItemsCallback);
+ if (!mHaveImageEditor) {
+ supportedOperations &= ~MediaObject.SUPPORT_EDIT;
+ }
+ }
+ MenuExecutor.updateMenuOperation(menu, supportedOperations);
+ }
+
+ private boolean canDoSlideShow() {
+ if (mMediaSet == null || mCurrentPhoto == null) {
+ return false;
+ }
+ if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
+ return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Action Bar show/hide management
+ //////////////////////////////////////////////////////////////////////////
+
+ private void showBars() {
+ if (mShowBars) return;
+ mShowBars = true;
+ mOrientationManager.unlockOrientation();
+ mActionBar.show();
+ mActivity.getGLRoot().setLightsOutMode(false);
+ refreshHidingMessage();
+ refreshBottomControlsWhenReady();
+ }
+
+ private void hideBars() {
+ if (!mShowBars) return;
+ mShowBars = false;
+ mActionBar.hide();
+ mActivity.getGLRoot().setLightsOutMode(true);
+ mHandler.removeMessages(MSG_HIDE_BARS);
+ refreshBottomControlsWhenReady();
+ }
+
+ private void refreshHidingMessage() {
+ mHandler.removeMessages(MSG_HIDE_BARS);
+ if (!mIsMenuVisible && !mPhotoView.getFilmMode()) {
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
+ }
+ }
+
+ private boolean canShowBars() {
+ // No bars if we are showing camera preview.
+ if (mAppBridge != null && mCurrentIndex == 0
+ && !mPhotoView.getFilmMode()) return false;
+
+ // No bars if it's not allowed.
+ if (!mActionBarAllowed) return false;
+
+ Configuration config = mActivity.getResources().getConfiguration();
+ if (config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void wantBars() {
+ if (canShowBars()) showBars();
+ }
+
+ private void toggleBars() {
+ if (mShowBars) {
+ hideBars();
+ } else {
+ if (canShowBars()) showBars();
+ }
+ }
+
+ private void updateBars() {
+ if (!canShowBars()) {
+ hideBars();
+ }
+ }
+
+ @Override
+ protected void onBackPressed() {
+ if (mShowDetails) {
+ hideDetails();
+ } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) {
+ // We are leaving this page. Set the result now.
+ setResult();
+ if (mStartInFilmstrip && !mPhotoView.getFilmMode()) {
+ mPhotoView.setFilmMode(true);
+ } else if (mTreatBackAsUp) {
+ onUpPressed();
+ } else {
+ super.onBackPressed();
+ }
+ }
+ }
+
+ private void onUpPressed() {
+ if ((mStartInFilmstrip || mAppBridge != null)
+ && !mPhotoView.getFilmMode()) {
+ mPhotoView.setFilmMode(true);
+ return;
+ }
+
+ if (mActivity.getStateManager().getStateCount() > 1) {
+ setResult();
+ super.onBackPressed();
+ return;
+ }
+
+ if (mOriginalSetPathString == null) return;
+
+ if (mAppBridge == null) {
+ // We're in view mode so set up the stacks on our own.
+ Bundle data = new Bundle(getData());
+ data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
+ data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
+ mActivity.getDataManager().getTopSetPath(
+ DataManager.INCLUDE_ALL));
+ mActivity.getStateManager().switchState(this, AlbumPage.class, data);
+ } else {
+ GalleryUtils.startGalleryActivity(mActivity);
+ }
+ }
+
+ private void setResult() {
+ Intent result = null;
+ result = new Intent();
+ result.putExtra(KEY_RETURN_INDEX_HINT, mCurrentIndex);
+ setStateResult(Activity.RESULT_OK, result);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AppBridge.Server interface
+ //////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void setCameraRelativeFrame(Rect frame) {
+ mPhotoView.setCameraRelativeFrame(frame);
+ }
+
+ @Override
+ public boolean switchWithCaptureAnimation(int offset) {
+ return mPhotoView.switchWithCaptureAnimation(offset);
+ }
+
+ @Override
+ public void setSwipingEnabled(boolean enabled) {
+ mPhotoView.setSwipingEnabled(enabled);
+ }
+
+ @Override
+ public void notifyScreenNailChanged() {
+ mScreenNailItem.setScreenNail(mAppBridge.attachScreenNail());
+ mScreenNailSet.notifyChange();
+ }
+
+ @Override
+ public void addSecureAlbumItem(boolean isVideo, int id) {
+ mSecureAlbum.addMediaItem(isVideo, id);
+ }
+
+ @Override
+ protected boolean onCreateActionBar(Menu menu) {
+ mActionBar.createActionBarMenu(R.menu.photo, menu);
+ mHaveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*");
+ updateMenuOperations();
+ mActionBar.setTitle(mMediaSet != null ? mMediaSet.getName() : "");
+ return true;
+ }
+
+ private MenuExecutor.ProgressListener mConfirmDialogListener =
+ new MenuExecutor.ProgressListener() {
+ @Override
+ public void onProgressUpdate(int index) {}
+
+ @Override
+ public void onProgressComplete(int result) {}
+
+ @Override
+ public void onConfirmDialogShown() {
+ mHandler.removeMessages(MSG_HIDE_BARS);
+ }
+
+ @Override
+ public void onConfirmDialogDismissed(boolean confirmed) {
+ refreshHidingMessage();
+ }
+
+ @Override
+ public void onProgressStart() {}
+ };
+
+ private void switchToGrid() {
+ if (mActivity.getStateManager().hasStateClass(AlbumPage.class)) {
+ onUpPressed();
+ } else {
+ if (mOriginalSetPathString == null) return;
+ if (mProgressBar != null) {
+ updateCurrentPhoto(null);
+ mProgressBar.hideProgress();
+ }
+ Bundle data = new Bundle(getData());
+ data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
+ data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
+ mActivity.getDataManager().getTopSetPath(
+ DataManager.INCLUDE_ALL));
+
+ // We only show cluster menu in the first AlbumPage in stack
+ // TODO: Enable this when running from the camera app
+ boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
+ data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum
+ && mAppBridge == null);
+
+ data.putBoolean(PhotoPage.KEY_APP_BRIDGE, mAppBridge != null);
+
+ // Account for live preview being first item
+ mActivity.getTransitionStore().put(KEY_RETURN_INDEX_HINT,
+ mAppBridge != null ? mCurrentIndex - 1 : mCurrentIndex);
+
+ if (mHasCameraScreennailOrPlaceholder && mAppBridge != null) {
+ mActivity.getStateManager().startState(AlbumPage.class, data);
+ } else {
+ mActivity.getStateManager().switchState(this, AlbumPage.class, data);
+ }
+ }
+ }
+
+ @Override
+ protected boolean onItemSelected(MenuItem item) {
+ if (mModel == null) return true;
+ refreshHidingMessage();
+ MediaItem current = mModel.getMediaItem(0);
+
+ // This is a shield for monkey when it clicks the action bar
+ // menu when transitioning from filmstrip to camera
+ if (current instanceof SnailItem) return true;
+ // TODO: We should check the current photo against the MediaItem
+ // that the menu was initially created for. We need to fix this
+ // after PhotoPage being refactored.
+ if (current == null) {
+ // item is not ready, ignore
+ return true;
+ }
+ int currentIndex = mModel.getCurrentIndex();
+ Path path = current.getPath();
+
+ DataManager manager = mActivity.getDataManager();
+ int action = item.getItemId();
+ String confirmMsg = null;
+ switch (action) {
+ case android.R.id.home: {
+ onUpPressed();
+ return true;
+ }
+ case R.id.action_slideshow: {
+ Bundle data = new Bundle();
+ data.putString(SlideshowPage.KEY_SET_PATH, mMediaSet.getPath().toString());
+ data.putString(SlideshowPage.KEY_ITEM_PATH, path.toString());
+ data.putInt(SlideshowPage.KEY_PHOTO_INDEX, currentIndex);
+ data.putBoolean(SlideshowPage.KEY_REPEAT, true);
+ mActivity.getStateManager().startStateForResult(
+ SlideshowPage.class, REQUEST_SLIDESHOW, data);
+ return true;
+ }
+ case R.id.action_crop: {
+ Activity activity = mActivity;
+ Intent intent = new Intent(CropActivity.CROP_ACTION);
+ intent.setClass(activity, CropActivity.class);
+ intent.setDataAndType(manager.getContentUri(path), current.getMimeType())
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
+ ? REQUEST_CROP_PICASA
+ : REQUEST_CROP);
+ return true;
+ }
+ case R.id.action_trim: {
+ Intent intent = new Intent(mActivity, TrimVideo.class);
+ intent.setData(manager.getContentUri(path));
+ // We need the file path to wrap this into a RandomAccessFile.
+ intent.putExtra(KEY_MEDIA_ITEM_PATH, current.getFilePath());
+ mActivity.startActivityForResult(intent, REQUEST_TRIM);
+ return true;
+ }
+ case R.id.action_mute: {
+ MuteVideo muteVideo = new MuteVideo(current.getFilePath(),
+ manager.getContentUri(path), mActivity);
+ muteVideo.muteInBackground();
+ return true;
+ }
+ case R.id.action_edit: {
+ launchPhotoEditor();
+ return true;
+ }
+ case R.id.action_simple_edit: {
+ launchSimpleEditor();
+ return true;
+ }
+ case R.id.action_details: {
+ if (mShowDetails) {
+ hideDetails();
+ } else {
+ showDetails();
+ }
+ return true;
+ }
+ case R.id.action_delete:
+ confirmMsg = mActivity.getResources().getQuantityString(
+ R.plurals.delete_selection, 1);
+ case R.id.action_setas:
+ case R.id.action_rotate_ccw:
+ case R.id.action_rotate_cw:
+ case R.id.action_show_on_map:
+ mSelectionManager.deSelectAll();
+ mSelectionManager.toggle(path);
+ mMenuExecutor.onMenuClicked(item, confirmMsg, mConfirmDialogListener);
+ return true;
+ default :
+ return false;
+ }
+ }
+
+ private void hideDetails() {
+ mShowDetails = false;
+ mDetailsHelper.hide();
+ }
+
+ private void showDetails() {
+ mShowDetails = true;
+ if (mDetailsHelper == null) {
+ mDetailsHelper = new DetailsHelper(mActivity, mRootPane, new MyDetailsSource());
+ mDetailsHelper.setCloseListener(new CloseListener() {
+ @Override
+ public void onClose() {
+ hideDetails();
+ }
+ });
+ }
+ mDetailsHelper.show();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Callbacks from PhotoView
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void onSingleTapUp(int x, int y) {
+ if (mAppBridge != null) {
+ if (mAppBridge.onSingleTapUp(x, y)) return;
+ }
+
+ MediaItem item = mModel.getMediaItem(0);
+ if (item == null || item == mScreenNailItem) {
+ // item is not ready or it is camera preview, ignore
+ return;
+ }
+
+ int supported = item.getSupportedOperations();
+ boolean playVideo = ((supported & MediaItem.SUPPORT_PLAY) != 0);
+ boolean unlock = ((supported & MediaItem.SUPPORT_UNLOCK) != 0);
+ boolean goBack = ((supported & MediaItem.SUPPORT_BACK) != 0);
+ boolean launchCamera = ((supported & MediaItem.SUPPORT_CAMERA_SHORTCUT) != 0);
+
+ if (playVideo) {
+ // determine if the point is at center (1/6) of the photo view.
+ // (The position of the "play" icon is at center (1/6) of the photo)
+ int w = mPhotoView.getWidth();
+ int h = mPhotoView.getHeight();
+ playVideo = (Math.abs(x - w / 2) * 12 <= w)
+ && (Math.abs(y - h / 2) * 12 <= h);
+ }
+
+ if (playVideo) {
+ if (mSecureAlbum == null) {
+ playVideo(mActivity, item.getPlayUri(), item.getName());
+ } else {
+ mActivity.getStateManager().finishState(this);
+ }
+ } else if (goBack) {
+ onBackPressed();
+ } else if (unlock) {
+ Intent intent = new Intent(mActivity, Gallery.class);
+ intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true);
+ mActivity.startActivity(intent);
+ } else if (launchCamera) {
+ launchCamera();
+ } else {
+ toggleBars();
+ }
+ }
+
+ @Override
+ public void onActionBarAllowed(boolean allowed) {
+ mActionBarAllowed = allowed;
+ mHandler.sendEmptyMessage(MSG_UPDATE_ACTION_BAR);
+ }
+
+ @Override
+ public void onActionBarWanted() {
+ mHandler.sendEmptyMessage(MSG_WANT_BARS);
+ }
+
+ @Override
+ public void onFullScreenChanged(boolean full) {
+ Message m = mHandler.obtainMessage(
+ MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
+ m.sendToTarget();
+ }
+
+ // How we do delete/undo:
+ //
+ // When the user choose to delete a media item, we just tell the
+ // FilterDeleteSet to hide that item. If the user choose to undo it, we
+ // again tell FilterDeleteSet not to hide it. If the user choose to commit
+ // the deletion, we then actually delete the media item.
+ @Override
+ public void onDeleteImage(Path path, int offset) {
+ onCommitDeleteImage(); // commit the previous deletion
+ mDeletePath = path;
+ mDeleteIsFocus = (offset == 0);
+ mMediaSet.addDeletion(path, mCurrentIndex + offset);
+ }
+
+ @Override
+ public void onUndoDeleteImage() {
+ if (mDeletePath == null) return;
+ // If the deletion was done on the focused item, we want the model to
+ // focus on it when it is undeleted.
+ if (mDeleteIsFocus) mModel.setFocusHintPath(mDeletePath);
+ mMediaSet.removeDeletion(mDeletePath);
+ mDeletePath = null;
+ }
+
+ @Override
+ public void onCommitDeleteImage() {
+ if (mDeletePath == null) return;
+ mMenuExecutor.startSingleItemAction(R.id.action_delete, mDeletePath);
+ mDeletePath = null;
+ }
+
+ public void playVideo(Activity activity, Uri uri, String title) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setDataAndType(uri, "video/*")
+ .putExtra(Intent.EXTRA_TITLE, title)
+ .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true);
+ activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(activity, activity.getString(R.string.video_err),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void setCurrentPhotoByIntent(Intent intent) {
+ if (intent == null) return;
+ Path path = mApplication.getDataManager()
+ .findPathByUri(intent.getData(), intent.getType());
+ if (path != null) {
+ Path albumPath = mApplication.getDataManager().getDefaultSetOf(path);
+ if (!albumPath.equalsIgnoreCase(mOriginalSetPathString)) {
+ // If the edited image is stored in a different album, we need
+ // to start a new activity state to show the new image
+ Bundle data = new Bundle(getData());
+ data.putString(KEY_MEDIA_SET_PATH, albumPath.toString());
+ data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString());
+ mActivity.getStateManager().startState(SinglePhotoPage.class, data);
+ return;
+ }
+ mModel.setCurrentPhoto(path, mCurrentIndex);
+ }
+ }
+
+ @Override
+ protected void onStateResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_CANCELED) {
+ // This is a reset, not a canceled
+ return;
+ }
+ if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
+ // Unmap reset vs. canceled
+ resultCode = Activity.RESULT_CANCELED;
+ }
+ mRecenterCameraOnResume = false;
+ switch (requestCode) {
+ case REQUEST_EDIT:
+ setCurrentPhotoByIntent(data);
+ break;
+ case REQUEST_CROP:
+ if (resultCode == Activity.RESULT_OK) {
+ setCurrentPhotoByIntent(data);
+ }
+ break;
+ case REQUEST_CROP_PICASA: {
+ if (resultCode == Activity.RESULT_OK) {
+ Context context = mActivity.getAndroidContext();
+ String message = context.getString(R.string.crop_saved,
+ context.getString(R.string.folder_edited_online_photos));
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ }
+ break;
+ }
+ case REQUEST_SLIDESHOW: {
+ if (data == null) break;
+ String path = data.getStringExtra(SlideshowPage.KEY_ITEM_PATH);
+ int index = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
+ if (path != null) {
+ mModel.setCurrentPhoto(Path.fromString(path), index);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mIsActive = false;
+
+ mActivity.getGLRoot().unfreeze();
+ mHandler.removeMessages(MSG_UNFREEZE_GLROOT);
+
+ DetailsHelper.pause();
+ // Hide the detail dialog on exit
+ if (mShowDetails) hideDetails();
+ if (mModel != null) {
+ mModel.pause();
+ }
+ mPhotoView.pause();
+ mHandler.removeMessages(MSG_HIDE_BARS);
+ mHandler.removeMessages(MSG_REFRESH_BOTTOM_CONTROLS);
+ refreshBottomControlsWhenReady();
+ mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener);
+ if (mShowSpinner) {
+ mActionBar.disableAlbumModeMenu(true);
+ }
+ onCommitDeleteImage();
+ mMenuExecutor.pause();
+ if (mMediaSet != null) mMediaSet.clearDeletion();
+ }
+
+ @Override
+ public void onCurrentImageUpdated() {
+ mActivity.getGLRoot().unfreeze();
+ }
+
+ @Override
+ public void onFilmModeChanged(boolean enabled) {
+ refreshBottomControlsWhenReady();
+ if (mShowSpinner) {
+ if (enabled) {
+ mActionBar.enableAlbumModeMenu(
+ GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this);
+ } else {
+ mActionBar.disableAlbumModeMenu(true);
+ }
+ }
+ if (enabled) {
+ mHandler.removeMessages(MSG_HIDE_BARS);
+ UsageStatistics.onContentViewChanged(
+ UsageStatistics.COMPONENT_GALLERY, "FilmstripPage");
+ } else {
+ refreshHidingMessage();
+ if (mAppBridge == null || mCurrentIndex > 0) {
+ UsageStatistics.onContentViewChanged(
+ UsageStatistics.COMPONENT_GALLERY, "SinglePhotoPage");
+ } else {
+ UsageStatistics.onContentViewChanged(
+ UsageStatistics.COMPONENT_CAMERA, "Unknown"); // TODO
+ }
+ }
+ }
+
+ private void transitionFromAlbumPageIfNeeded() {
+ TransitionStore transitions = mActivity.getTransitionStore();
+
+ int albumPageTransition = transitions.get(
+ KEY_ALBUMPAGE_TRANSITION, MSG_ALBUMPAGE_NONE);
+
+ if (albumPageTransition == MSG_ALBUMPAGE_NONE && mAppBridge != null
+ && mRecenterCameraOnResume) {
+ // Generally, resuming the PhotoPage when in Camera should
+ // reset to the capture mode to allow quick photo taking
+ mCurrentIndex = 0;
+ mPhotoView.resetToFirstPicture();
+ } else {
+ int resumeIndex = transitions.get(KEY_INDEX_HINT, -1);
+ if (resumeIndex >= 0) {
+ if (mHasCameraScreennailOrPlaceholder) {
+ // Account for preview/placeholder being the first item
+ resumeIndex++;
+ }
+ if (resumeIndex < mMediaSet.getMediaItemCount()) {
+ mCurrentIndex = resumeIndex;
+ mModel.moveTo(mCurrentIndex);
+ }
+ }
+ }
+
+ if (albumPageTransition == MSG_ALBUMPAGE_RESUMED) {
+ mPhotoView.setFilmMode(mStartInFilmstrip || mAppBridge != null);
+ } else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) {
+ mPhotoView.setFilmMode(false);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (mModel == null) {
+ mActivity.getStateManager().finishState(this);
+ return;
+ }
+ transitionFromAlbumPageIfNeeded();
+
+ mActivity.getGLRoot().freeze();
+ mIsActive = true;
+ setContentPane(mRootPane);
+
+ mModel.resume();
+ mPhotoView.resume();
+ mActionBar.setDisplayOptions(
+ ((mSecureAlbum == null) && (mSetPathString != null)), false);
+ mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener);
+ refreshBottomControlsWhenReady();
+ if (mShowSpinner && mPhotoView.getFilmMode()) {
+ mActionBar.enableAlbumModeMenu(
+ GalleryActionBar.ALBUM_FILMSTRIP_MODE_SELECTED, this);
+ }
+ if (!mShowBars) {
+ mActionBar.hide();
+ mActivity.getGLRoot().setLightsOutMode(true);
+ }
+ boolean haveImageEditor = GalleryUtils.isEditorAvailable(mActivity, "image/*");
+ if (haveImageEditor != mHaveImageEditor) {
+ mHaveImageEditor = haveImageEditor;
+ updateMenuOperations();
+ }
+
+ mRecenterCameraOnResume = true;
+ mHandler.sendEmptyMessageDelayed(MSG_UNFREEZE_GLROOT, UNFREEZE_GLROOT_TIMEOUT);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mAppBridge != null) {
+ mAppBridge.setServer(null);
+ mScreenNailItem.setScreenNail(null);
+ mAppBridge.detachScreenNail();
+ mAppBridge = null;
+ mScreenNailSet = null;
+ mScreenNailItem = null;
+ }
+ mActivity.getGLRoot().setOrientationSource(null);
+ if (mBottomControls != null) mBottomControls.cleanup();
+
+ // Remove all pending messages.
+ mHandler.removeCallbacksAndMessages(null);
+ super.onDestroy();
+ }
+
+ private class MyDetailsSource implements DetailsSource {
+
+ @Override
+ public MediaDetails getDetails() {
+ return mModel.getMediaItem(0).getDetails();
+ }
+
+ @Override
+ public int size() {
+ return mMediaSet != null ? mMediaSet.getMediaItemCount() : 1;
+ }
+
+ @Override
+ public int setIndex() {
+ return mModel.getCurrentIndex();
+ }
+ }
+
+ @Override
+ public void onAlbumModeSelected(int mode) {
+ if (mode == GalleryActionBar.ALBUM_GRID_MODE_SELECTED) {
+ switchToGrid();
+ }
+ }
+
+ @Override
+ public void refreshBottomControlsWhenReady() {
+ if (mBottomControls == null) {
+ return;
+ }
+ MediaObject currentPhoto = mCurrentPhoto;
+ if (currentPhoto == null) {
+ mHandler.obtainMessage(MSG_REFRESH_BOTTOM_CONTROLS, 0, 0, currentPhoto).sendToTarget();
+ } else {
+ currentPhoto.getPanoramaSupport(mRefreshBottomControlsCallback);
+ }
+ }
+
+ private void updatePanoramaUI(boolean isPanorama360) {
+ Menu menu = mActionBar.getMenu();
+
+ // it could be null if onCreateActionBar has not been called yet
+ if (menu == null) {
+ return;
+ }
+
+ MenuExecutor.updateMenuForPanorama(menu, isPanorama360, isPanorama360);
+
+ if (isPanorama360) {
+ MenuItem item = menu.findItem(R.id.action_share);
+ if (item != null) {
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ item.setTitle(mActivity.getResources().getString(R.string.share_as_photo));
+ }
+ } else if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_SHARE) != 0) {
+ MenuItem item = menu.findItem(R.id.action_share);
+ if (item != null) {
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ item.setTitle(mActivity.getResources().getString(R.string.share));
+ }
+ }
+ }
+
+ @Override
+ public void onUndoBarVisibilityChanged(boolean visible) {
+ refreshBottomControlsWhenReady();
+ }
+
+ @Override
+ public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
+ final long timestampMillis = mCurrentPhoto.getDateInMs();
+ final String mediaType = getMediaTypeString(mCurrentPhoto);
+ UsageStatistics.onEvent(UsageStatistics.COMPONENT_GALLERY,
+ UsageStatistics.ACTION_SHARE,
+ mediaType,
+ timestampMillis > 0
+ ? System.currentTimeMillis() - timestampMillis
+ : -1);
+ return false;
+ }
+
+ private static String getMediaTypeString(MediaItem item) {
+ if (item.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO) {
+ return "Video";
+ } else if (item.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE) {
+ return "Photo";
+ } else {
+ return "Unknown:" + item.getMediaType();
+ }
+ }
+
+}