| /* |
| * 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.Activity; |
| import android.content.ActivityNotFoundException; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.Rect; |
| import android.net.Uri; |
| import android.nfc.NfcAdapter; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.animation.AccelerateInterpolator; |
| import android.widget.Toast; |
| |
| import com.android.gallery3d.R; |
| import com.android.gallery3d.actionbar.ActionBarInterface.OnMenuVisibilityListener; |
| import com.android.gallery3d.anim.FloatAnimation; |
| import com.android.gallery3d.common.ApiHelper; |
| import com.android.gallery3d.common.Utils; |
| import com.android.gallery3d.data.DataManager; |
| import com.android.gallery3d.data.FilterDeleteSet; |
| import com.android.gallery3d.data.MediaDetails; |
| import com.android.gallery3d.data.MediaItem; |
| import com.android.gallery3d.data.MediaObject; |
| import com.android.gallery3d.data.MediaSet; |
| import com.android.gallery3d.data.MtpSource; |
| import com.android.gallery3d.data.Path; |
| import com.android.gallery3d.data.SnailAlbum; |
| import com.android.gallery3d.data.SnailItem; |
| import com.android.gallery3d.data.SnailSource; |
| import com.android.gallery3d.picasasource.PicasaSource; |
| import com.android.gallery3d.ui.AnimationTime; |
| 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.GLCanvas; |
| import com.android.gallery3d.ui.GLRoot; |
| import com.android.gallery3d.ui.GLRoot.OnGLIdleListener; |
| import com.android.gallery3d.ui.BitmapScreenNail; |
| import com.android.gallery3d.ui.GLView; |
| import com.android.gallery3d.ui.ImportCompleteListener; |
| import com.android.gallery3d.ui.MenuExecutor; |
| import com.android.gallery3d.ui.PhotoFallbackEffect; |
| import com.android.gallery3d.ui.PhotoView; |
| import com.android.gallery3d.ui.RawTexture; |
| import com.android.gallery3d.ui.SelectionManager; |
| import com.android.gallery3d.ui.SynchronizedHandler; |
| import com.android.gallery3d.util.GalleryUtils; |
| import com.android.gallery3d.util.MediaSetUtils; |
| |
| public class PhotoPage extends ActivityState implements |
| PhotoView.Listener, OrientationManager.Listener, AppBridge.Server { |
| private static final String TAG = "PhotoPage"; |
| |
| private static final int MSG_HIDE_BARS = 1; |
| private static final int MSG_LOCK_ORIENTATION = 2; |
| private static final int MSG_UNLOCK_ORIENTATION = 3; |
| 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 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; |
| |
| 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_RETURN_INDEX_HINT = "return-index-hint"; |
| |
| private GalleryApp mApplication; |
| private SelectionManager mSelectionManager; |
| |
| private PhotoView mPhotoView; |
| private PhotoPage.Model mModel; |
| private DetailsHelper mDetailsHelper; |
| private boolean mShowDetails; |
| private Path mPendingSharePath; |
| |
| // mMediaSet could be null if there is no KEY_MEDIA_SET_PATH supplied. |
| // E.g., viewing a photo in gmail attachment |
| private FilterDeleteSet mMediaSet; |
| |
| private int mCurrentIndex = 0; |
| private Handler mHandler; |
| private boolean mShowBars = true; |
| private volatile boolean mActionBarAllowed = true; |
| private GalleryActionBar mActionBar; |
| private boolean mIsMenuVisible; |
| private MediaItem mCurrentPhoto = null; |
| private MenuExecutor mMenuExecutor; |
| private boolean mIsActive; |
| 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 mHasActivityResult; |
| private boolean mTreatBackAsUp; |
| |
| private RawTexture mFadeOutTexture; |
| private Rect mOpenAnimationRect; |
| public static final int ANIM_TIME_OPENING = 300; |
| |
| // 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 NfcAdapter mNfcAdapter; |
| |
| private final MyMenuVisibilityListener mMenuVisibilityListener = |
| new MyMenuVisibilityListener(); |
| |
| 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 static class BackgroundFadeOut extends FloatAnimation { |
| public BackgroundFadeOut() { |
| super(1f, 0f, ANIM_TIME_OPENING); |
| setInterpolator(new AccelerateInterpolator(2f)); |
| } |
| } |
| |
| private final FloatAnimation mBackgroundFade = new BackgroundFadeOut(); |
| |
| private final GLView mRootPane = new GLView() { |
| @Override |
| protected void renderBackground(GLCanvas view) { |
| if(mFadeOutTexture != null) { |
| if(mBackgroundFade.calculate(AnimationTime.get())) invalidate(); |
| if(!mBackgroundFade.isActive()) { |
| mFadeOutTexture = null; |
| mOpenAnimationRect = null; |
| BitmapScreenNail.enableDrawPlaceholder(); |
| } else { |
| float fadeAlpha = mBackgroundFade.get(); |
| if(fadeAlpha < 1f) { |
| view.clearBuffer(0f, 0f, 0f, 1f); |
| view.setAlpha(fadeAlpha); |
| } |
| mFadeOutTexture.draw(view, 0, 0); |
| view.setAlpha(1f); |
| return; |
| } |
| } |
| view.clearBuffer(0f, 0f, 0f, 1f); |
| } |
| |
| @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) { |
| 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(); |
| mOrientationManager.addListener(this); |
| mActivity.getGLRoot().setOrientationSource(mOrientationManager); |
| |
| mSetPathString = data.getString(KEY_MEDIA_SET_PATH); |
| mOriginalSetPathString = mSetPathString; |
| mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity.getAndroidContext()); |
| Path itemPath = Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH)); |
| mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false); |
| |
| if (mSetPathString != null) { |
| mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE); |
| if (mAppBridge != null) { |
| mAppBridge.setServer(this); |
| mOrientationManager.lockOrientation(); |
| |
| // 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()); |
| |
| // Combine the original MediaSet with the one for ScreenNail |
| // from AppBridge. |
| mSetPathString = "/combo/item/{" + screenNailSetPath + |
| "," + mSetPathString + "}"; |
| |
| // Start from the screen nail. |
| itemPath = screenNailItemPath; |
| |
| // Action bar should not be displayed when camera starts. |
| mFlags |= FLAG_HIDE_ACTION_BAR | FLAG_HIDE_STATUS_BAR; |
| mShowBars = false; |
| } |
| |
| MediaSet originalSet = mActivity.getDataManager() |
| .getMediaSet(mSetPathString); |
| mSelectionManager.setSourceMediaSet(originalSet); |
| mSetPathString = "/filter/delete/{" + mSetPathString + "}"; |
| mMediaSet = (FilterDeleteSet) mActivity.getDataManager() |
| .getMediaSet(mSetPathString); |
| mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0); |
| if (mMediaSet == null) { |
| Log.w(TAG, "failed to restore " + mSetPathString); |
| } |
| 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) { |
| mCurrentIndex = index; |
| if (item != null) { |
| MediaItem photo = mModel.getMediaItem(0); |
| if (photo != null) updateCurrentPhoto(photo); |
| } |
| updateBars(); |
| } |
| |
| @Override |
| public void onLoadingFinished() { |
| 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); |
| } |
| |
| mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { |
| @Override |
| public void handleMessage(Message message) { |
| switch (message.what) { |
| case MSG_HIDE_BARS: { |
| hideBars(); |
| break; |
| } |
| case MSG_LOCK_ORIENTATION: { |
| mOrientationManager.lockOrientation(); |
| break; |
| } |
| case MSG_UNLOCK_ORIENTATION: { |
| mOrientationManager.unlockOrientation(); |
| break; |
| } |
| case MSG_ON_FULL_SCREEN_CHANGED: { |
| 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; |
| } |
| default: throw new AssertionError(message.what); |
| } |
| } |
| }; |
| |
| // start the opening animation only if it's not restored. |
| if (restoreState == null) { |
| mFadeOutTexture = mActivity.getTransitionStore().get(AlbumPage.KEY_FADE_TEXTURE); |
| if(mFadeOutTexture != null) { |
| mBackgroundFade.start(); |
| BitmapScreenNail.disableDrawPlaceholder(); |
| mOpenAnimationRect = (Rect) data.getParcelable(KEY_OPEN_ANIMATION_RECT); |
| mPhotoView.setOpenAnimationRect(mOpenAnimationRect); |
| } |
| } |
| } |
| |
| @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) |
| private void setNfcBeamPushUris(Uri[] uris) { |
| if (mNfcAdapter != null && ApiHelper.HAS_SET_BEAM_PUSH_URIS) { |
| mNfcAdapter.setBeamPushUris(uris, (Activity)mActivity); |
| } |
| } |
| |
| private Intent createShareIntent(Path path) { |
| DataManager manager = mActivity.getDataManager(); |
| int type = manager.getMediaType(path); |
| Intent intent = new Intent(Intent.ACTION_SEND); |
| intent.setType(MenuExecutor.getMimeType(type)); |
| Uri uri = manager.getContentUri(path); |
| intent.putExtra(Intent.EXTRA_STREAM, uri); |
| return intent; |
| |
| } |
| |
| private void updateShareURI(Path path) { |
| if (mActionBar.hasShareMenuItem()) { |
| DataManager manager = mActivity.getDataManager(); |
| Uri uri = manager.getContentUri(path); |
| mActionBar.setShareIntent(createShareIntent(path)); |
| setNfcBeamPushUris(new Uri[]{uri}); |
| mPendingSharePath = null; |
| } else { |
| // This happens when ActionBar is not created yet. |
| mPendingSharePath = path; |
| } |
| } |
| |
| private void updateCurrentPhoto(MediaItem photo) { |
| if (mCurrentPhoto == photo) return; |
| mCurrentPhoto = photo; |
| if (mCurrentPhoto == null) return; |
| updateMenuOperations(); |
| updateTitle(); |
| if (mShowDetails) { |
| mDetailsHelper.reloadDetails(); |
| } |
| if ((photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { |
| updateShareURI(photo.getPath()); |
| } |
| } |
| |
| private void updateTitle() { |
| if (mCurrentPhoto == null) return; |
| boolean showTitle = mActivity.getAndroidContext().getResources().getBoolean( |
| R.bool.show_action_bar_title); |
| if (showTitle && mCurrentPhoto.getName() != null) { |
| mActionBar.setTitle(mCurrentPhoto.getName()); |
| } else { |
| mActionBar.setTitle(""); |
| } |
| } |
| |
| private void updateMenuOperations() { |
| mActionBar.setMenuItemVisible(R.id.action_slideshow, canDoSlideShow()); |
| if (mCurrentPhoto == null) return; |
| int supportedOperations = mCurrentPhoto.getSupportedOperations(); |
| if (!GalleryUtils.isEditorAvailable((Context) mActivity, "image/*")) { |
| supportedOperations &= ~MediaObject.SUPPORT_EDIT; |
| } |
| MenuExecutor.updateMenuOperation(mActionBar, supportedOperations); |
| } |
| |
| private boolean canDoSlideShow() { |
| if (mMediaSet == null || mCurrentPhoto == null) { |
| return false; |
| } |
| if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) { |
| return false; |
| } |
| if (MtpSource.isMtpPath(mOriginalSetPathString)) { |
| 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(); |
| } |
| |
| private void hideBars() { |
| if (!mShowBars) return; |
| mShowBars = false; |
| mActionBar.hide(); |
| mActivity.getGLRoot().setLightsOutMode(true); |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| } |
| |
| private void refreshHidingMessage() { |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| if (!mIsMenuVisible) { |
| mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT); |
| } |
| } |
| |
| private boolean canShowBars() { |
| // No bars if we are showing camera preview. |
| if (mAppBridge != null && mCurrentIndex == 0) return false; |
| // No bars if it's not allowed. |
| if (!mActionBarAllowed) 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 |
| public void onOrientationCompensationChanged() { |
| mActivity.getGLRoot().requestLayoutContentPane(); |
| } |
| |
| @Override |
| protected void onBackPressed() { |
| if (mShowDetails) { |
| hideDetails(); |
| } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) { |
| // We are leaving this page. Set the result now. |
| setResult(); |
| if (mTreatBackAsUp) { |
| onUpPressed(); |
| } else { |
| super.onBackPressed(); |
| } |
| } |
| } |
| |
| private void onUpPressed() { |
| if (mActivity.getStateManager().getStateCount() > 1) { |
| 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 { |
| // Start the real gallery activity to view the camera roll. |
| Uri uri = Uri.parse("content://media/external/file?bucketId=" |
| + MediaSetUtils.CAMERA_BUCKET_ID); |
| Intent intent = new Intent(Intent.ACTION_VIEW); |
| intent.setDataAndType(uri, ContentResolver.CURSOR_DIR_BASE_TYPE + "/image"); |
| ((Activity) mActivity).startActivity(intent); |
| } |
| } |
| |
| private void setResult() { |
| Intent result = null; |
| if (!mPhotoView.getFilmMode()) { |
| 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 |
| protected boolean onCreateActionBar(Menu menu) { |
| GalleryActionBar actionBar = mActionBar; |
| boolean result = actionBar.createActionMenu(menu, R.menu.photo); |
| if (mPendingSharePath != null) updateShareURI(mPendingSharePath); |
| updateMenuOperations(); |
| updateTitle(); |
| return result; |
| } |
| |
| 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() {} |
| }; |
| |
| @Override |
| protected boolean onItemSelected(MenuItem item) { |
| refreshHidingMessage(); |
| MediaItem current = mModel.getMediaItem(0); |
| |
| 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 = (Activity) mActivity; |
| Intent intent = new Intent(CropImage.CROP_ACTION); |
| intent.setClass(activity, CropImage.class); |
| intent.setData(manager.getContentUri(path)); |
| activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current) |
| ? REQUEST_CROP_PICASA |
| : REQUEST_CROP); |
| return true; |
| } |
| case R.id.action_edit: { |
| Intent intent = new Intent(Intent.ACTION_EDIT) |
| .setData(manager.getContentUri(path)) |
| .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| ((Activity) mActivity).startActivityForResult(Intent.createChooser(intent, null), |
| REQUEST_EDIT); |
| 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; |
| case R.id.action_import: |
| mSelectionManager.deSelectAll(); |
| mSelectionManager.toggle(path); |
| mMenuExecutor.onMenuClicked(item, confirmMsg, |
| new ImportCompleteListener(mActivity)); |
| return true; |
| case R.id.action_share: |
| Activity activity = (Activity) mActivity; |
| Intent intent = createShareIntent(mCurrentPhoto.getPath()); |
| activity.startActivity(Intent.createChooser(intent, |
| activity.getString(R.string.share))); |
| 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; |
| } |
| |
| boolean playVideo = |
| (item.getSupportedOperations() & MediaItem.SUPPORT_PLAY) != 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) { |
| playVideo((Activity) mActivity, item.getPlayUri(), item.getName()); |
| } else { |
| toggleBars(); |
| } |
| } |
| |
| @Override |
| public void lockOrientation() { |
| mHandler.sendEmptyMessage(MSG_LOCK_ORIENTATION); |
| } |
| |
| @Override |
| public void unlockOrientation() { |
| mHandler.sendEmptyMessage(MSG_UNLOCK_ORIENTATION); |
| } |
| |
| @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; |
| mSelectionManager.deSelectAll(); |
| mSelectionManager.toggle(mDeletePath); |
| mMenuExecutor.onMenuClicked(R.id.action_delete, null, true, false); |
| mDeletePath = null; |
| } |
| |
| public static 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) { |
| mModel.setCurrentPhoto(path, mCurrentIndex); |
| } |
| } |
| |
| @Override |
| protected void onStateResult(int requestCode, int resultCode, Intent data) { |
| mHasActivityResult = true; |
| 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_download)); |
| 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 |
| protected void clearStateResult() { |
| mHasActivityResult = false; |
| } |
| |
| private class PreparePhotoFallback implements OnGLIdleListener { |
| private PhotoFallbackEffect mPhotoFallback = new PhotoFallbackEffect(); |
| private boolean mResultReady = false; |
| |
| public synchronized PhotoFallbackEffect get() { |
| while (!mResultReady) { |
| Utils.waitWithoutInterrupt(this); |
| } |
| return mPhotoFallback; |
| } |
| |
| @Override |
| public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) { |
| mPhotoFallback = mPhotoView.buildFallbackEffect(mRootPane, canvas); |
| synchronized (this) { |
| mResultReady = true; |
| notifyAll(); |
| } |
| return false; |
| } |
| } |
| |
| private void preparePhotoFallbackView() { |
| GLRoot root = mActivity.getGLRoot(); |
| PreparePhotoFallback task = new PreparePhotoFallback(); |
| root.unlockRenderThread(); |
| PhotoFallbackEffect anim; |
| try { |
| root.addOnGLIdleListener(task); |
| anim = task.get(); |
| } finally { |
| root.lockRenderThread(); |
| } |
| mActivity.getTransitionStore().put( |
| AlbumPage.KEY_RESUME_ANIMATION, anim); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| mIsActive = false; |
| |
| mActivity.getGLRoot().unfreeze(); |
| mHandler.removeMessages(MSG_UNFREEZE_GLROOT); |
| if (isFinishing()) preparePhotoFallbackView(); |
| |
| DetailsHelper.pause(); |
| mPhotoView.pause(); |
| mModel.pause(); |
| mHandler.removeMessages(MSG_HIDE_BARS); |
| mActionBar.removeOnMenuVisibilityListener(mMenuVisibilityListener); |
| |
| onCommitDeleteImage(); |
| mMenuExecutor.pause(); |
| if (mMediaSet != null) mMediaSet.clearDeletion(); |
| } |
| |
| @Override |
| public void onCurrentImageUpdated() { |
| mActivity.getGLRoot().unfreeze(); |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| mActivity.getGLRoot().freeze(); |
| mIsActive = true; |
| setContentPane(mRootPane); |
| |
| mModel.resume(); |
| mPhotoView.resume(); |
| mActionBar.setDisplayOptions(mSetPathString != null, true); |
| mActionBar.addOnMenuVisibilityListener(mMenuVisibilityListener); |
| |
| if (mAppBridge != null && !mHasActivityResult) { |
| mPhotoView.resetToFirstPicture(); |
| } |
| mHasActivityResult = false; |
| 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; |
| } |
| mOrientationManager.removeListener(this); |
| mActivity.getGLRoot().setOrientationSource(null); |
| |
| // 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(); |
| } |
| } |
| } |